Android 记录一下仿抖音评论列表

之前写过一篇类似的文章,但是只有dialog的部分,链接:https://www.jianshu.com/p/c1941eee0c4a

1636094985044.gif

当时想的是难点在框框,其他的都还好,什么二级评论写起来容易,以后遇到也不怕,所以没进一步写,结果现在看别人的东西,自己又想去写的时候,发现不一般啊,第一天的时候居然没写出来。所以记录一下,目的不为别的,就怕以后在遇到,能拿过来就用。
demo的功能范围:
1、评论框BottomSheetDialogFragment的设置,能滑动隐藏dialog。
2、一级评论二级评论的加载,二级评论的加载更多与收起功能。
3、评论记载失败一级加载更多处理。
4、发送模拟新主评论,以及回复他人评论

这次主要做二级评论,和抖音一样能加载跟多子评论加载完全部,能收起。


image.png

demo的设定是一条一级评论,如果他有二级评论,并且二级评论数量超过1条时就会出现加载更多条目。

我分析的结论是有两种做法:
1、对于一条评论体来说,我们可以把里面的二级评论看做是一个子recycleview,二级评论的加载更多条目可以用底部布局来写,就相当于一个大recycleview来展示所有一级评论,然后每一条一级评论里面可能有一个次recycleview。这样做的话,就是recycleview的复用需要好好处理了。
2、不采用嵌套的recycleview的方式,把一级评论、二级评论加载更多条目三种都看做是recycleview的条目,给他们设定itemType类型,分别展示。这个需要处理好加载更多时,插入位置的计算。
权衡利弊,demo采用了第二种。第一种我也会去做,但是可能不会发出来。

说到给recycleview分类,那肯定用BaseRecyclerViewAdapterHelper更方便。可惜的是项目里面用的是2.x的版本,没有3.x的,它的3.x版本有node模式,解决这种二级列表问题更快,demo为了更好的模拟项目,也就用了2.x版本。

数据方面,好好思考一下一级,二级,加载更多的javabean结构。这里我模拟了公司后台返回的数据模型。
一级评论:MainCommendBean
数据体:返回评论文字内容、所有子评论数量、当前默认需要展示的二级评论集合(默认先展示一条二级评论,所以默认集合大小为1)。
二级评论:ChildCommendBean
数据体:返回评论文字内容
加载更多评论条目:MoreCommendBean(这个后台不会返回,是我们拿到数据后自己往数据里面加,主要是为了持有一级评论,方便逻辑判断)
数据体:对应的一级评论引用、点击加载更多二级评论存放的集合。

好累啊,直接上代码把。
主要评论框:CommendMsgDialogFragment

public class CommendMsgDialogFragment extends BottomSheetDialogFragment implements OnClickLoadMoreChildCallBack, View.OnClickListener, OnCommendItemClickCallBack {
    private OnDisMissCallBack onDisMissCallBack;
    private RecyclerView mRecyclerView;
    private int page = 0;
    private int pageNum = 20;
    private View emptyView;
    private DataTestUtil dataTestUtil;
    private CommendAdapter mAdapter;
    private TextView mTextView;
    private CommendInputDialogFragment mCommendInputDialog;
    private TextView mTvSend;

    public void setOnDisMissCallBack(OnDisMissCallBack onDisMissCallBack) {
        this.onDisMissCallBack = onDisMissCallBack;
    }

    public static CommendMsgDialogFragment newInstance() {

        Bundle args = new Bundle();

        CommendMsgDialogFragment fragment = new CommendMsgDialogFragment();
        fragment.setArguments(args);
        return fragment;
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(BottomSheetDialogFragment.STYLE_NORMAL, R.style.CustomBottomSheetDialogTheme);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_commend_msg_dialog, container, false);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        Window window = getDialog().getWindow();
        window.requestFeature(Window.FEATURE_NO_TITLE);
        super.onActivityCreated(savedInstanceState);
        //设置背景为透明
        FrameLayout bottom = getDialog().findViewById(R.id.design_bottom_sheet);
        if (bottom != null) {
            bottom.setBackgroundResource(android.R.color.transparent);
        }
        //去除阴影
        window.setGravity(Gravity.BOTTOM);
        WindowManager.LayoutParams layoutParams = window.getAttributes();
        layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
        layoutParams.dimAmount = 0.0f;
        window.setAttributes(layoutParams);
        getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                if(onDisMissCallBack != null){
                    onDisMissCallBack.onDismiss();
                }
                dismissAllowingStateLoss();
            }
        });
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        dataTestUtil = new DataTestUtil();//初始模拟数据工具
        initView(view);
        getMainCommends(true);
    }

    private void initView(View view) {
        emptyView = LayoutInflater.from(getContext()).inflate(R.layout.empty_view, null, false);
        mRecyclerView = view.findViewById(R.id.mRecyclerView);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));//加载更多
        mAdapter = new CommendAdapter(null);
        mAdapter.setLoadMoreView(new LoadMoreAndFooterView());
        mAdapter.setOnClickLoadMoreChildCallBack(this);
        mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {
            @Override
            public void onLoadMoreRequested() {
                getMainCommends(false);
            }
        });
        mAdapter.setOnCommendItemClickCallBack(this);
        mRecyclerView.setAdapter(mAdapter);
        mTextView = view.findViewById(R.id.mTextView);
        mTextView.setOnClickListener(this);
        mTvSend = view.findViewById(R.id.mTvSend);
        mTvSend.setOnClickListener(this);
    }

    /**
     * 获取主评论
     */
    private void getMainCommends(boolean isRefresh) {
        if(isRefresh){
            CommendMsgDialogFragment.this.page = 0;
        }
        dataTestUtil.getMainCommends(isRefresh, page, pageNum, new OnMainCommendCallBack() {
            @Override
            public void onSuccess(boolean isRefresh, List<MainCommendBean> list) {
                CommendMsgDialogFragment.this.page += 1;
                List<MultiItemEntity> newList = getMutilItems(list);
                if(isRefresh){
                    mAdapter.setNewData(newList);
                }else{
                    mAdapter.addData(newList);
                }
                mAdapter.expandAll();
                mAdapter.loadMoreComplete();
            }

            @Override
            public void onFail() {
                if(isRefresh){
                    mAdapter.setEmptyView(emptyView);
                }else{
                    mAdapter.loadMoreEnd();
                }
            }
        });
    }

    /**
     * 转换并且设置二级数据
     * @param list
     * @return
     */
    private List<MultiItemEntity> getMutilItems(List<MainCommendBean> list) {
        List<MultiItemEntity> datas = new ArrayList<>();
        for (MainCommendBean mainCommendBean : list) {
            if(mainCommendBean != null && mainCommendBean.getChildList() != null && mainCommendBean.getChildList().size() > 0){
                //如果有子回复,需要绑定
                for (ChildCommendBean childCommendBean : mainCommendBean.getChildList()) {
                    mainCommendBean.addSubItem(childCommendBean);
                }
                //最后加一条加载更多条目
                MoreCommendBean moreCommendBean = new MoreCommendBean();
                moreCommendBean.setMainCommendBean(mainCommendBean);
                mainCommendBean.addSubItem(moreCommendBean);
            }
            datas.add(mainCommendBean);
        }
        return datas;
    }

    /**
     * 点击展示更多子评论条目
     * @param tvExpandNum
     * @param loadingImageView
     * @param moreCommendBean
     */
    @Override
    public void onClickMoreChild(TextView tvExpandNum, ProgressBar loadingImageView, MoreCommendBean moreCommendBean) {
        if(moreCommendBean.isLoaded()){
            loadChildCommendFromLocal(moreCommendBean);
        }else{
            loadChildCommendFromNet(moreCommendBean,tvExpandNum,loadingImageView);
        }
    }

    /**
     * 网络上的数据加载
     * @param moreCommendBean
     */
    private void loadChildCommendFromNet(MoreCommendBean moreCommendBean,TextView tvExpandNum, ProgressBar loadingImageView) {
        showChileLoading(true,tvExpandNum,loadingImageView,moreCommendBean);
        dataTestUtil.getChildCommends(moreCommendBean.getMainCommendBean().getChildList().size(), moreCommendBean.getMainCommendBean().getChildNumber(), new OnChildCommendCallBack() {
            @Override
            public void onSuccess(List<ChildCommendBean> list) {
                //没加载完全部,就去网络
                moreCommendBean.getMainCommendBean().getChildList().addAll(list);
                moreCommendBean.getSubList().addAll(list);
                int index = mAdapter.getData().indexOf(moreCommendBean);
                mAdapter.addData(index,list);
                if(moreCommendBean.getMainCommendBean().getChildList().size() == moreCommendBean.getMainCommendBean().getChildNumber()){
                    //全部加载完了
                    moreCommendBean.setZheDie(false);
                    moreCommendBean.setLoaded(true);
                }
                mAdapter.notifyItemChanged(index+list.size());
                showChileLoading(false,tvExpandNum,loadingImageView,moreCommendBean);
            }
        });
    }

    /**
     * 网上数据全部加载完了,下次再点击暂开收起,数据就是本地的
     * 新加入的子评论都在moreCommendBean.getSubList里面
     */
    private void loadChildCommendFromLocal(MoreCommendBean moreCommendBean) {
        if(moreCommendBean.isZheDie()){
            //加载完了,就不要网络加载了,去本地
            int index = mAdapter.getData().indexOf(moreCommendBean);
            //去除最先加入的subitem
            mAdapter.addData(index,moreCommendBean.getSubList());
            moreCommendBean.setZheDie(false);
        }else{
            //全部展开了,点击需要收起
            //移除就行了
            int start = mAdapter.getData().indexOf(moreCommendBean)-1;
            int end = start - moreCommendBean.getSubList().size();
            for(int x = start; x > end; x--){
                mAdapter.remove(x);
            }
            moreCommendBean.setZheDie(true);
        }
    }

    private void showChileLoading(boolean show, TextView tvExpandNum, ProgressBar loadingImageView, MoreCommendBean moreCommendBean){
        if(show){
            tvExpandNum.setVisibility(View.GONE);
            loadingImageView.setVisibility(View.VISIBLE);
            moreCommendBean.isLoading = true;
        }else{
            tvExpandNum.setVisibility(View.VISIBLE);
            loadingImageView.setVisibility(View.GONE);
            moreCommendBean.isLoading = false;
        }
    }

    private void showInputDialog(MainCommendBean mainCommendBean) {
        if(mCommendInputDialog == null){
            mCommendInputDialog = CommendInputDialogFragment.newInstance(mTextView.getText().toString());
            mCommendInputDialog.setOnDisMissCallBack(new CommendInputDialogFragment.OnDisMissCallBack() {
                @Override
                public void onDismiss() {
                    mCommendInputDialog = null;
                }

                @Override
                public void saveCommend() {
                    onCommitCommend(mainCommendBean);
                }

                @Override
                public void onCommendTextChanged(String contend) {
                    mTextView.setText(contend);
                }
            });
            mCommendInputDialog.show(getChildFragmentManager(), mCommendInputDialog.getTag());
        }
    }

    private void onCommitCommend(MainCommendBean mainCommendBean) {
        //如果mainCommendBean为空,表示回复全部人,提交的评论加到主评论的第一个
        //如果不为空,说明是插入到某一个主评论下的第一个评论
        String content = mTextView.getText().toString();
        if(TextUtils.isEmpty(content)){
            Toast.makeText(App.getInstance(), "数据不能为空", Toast.LENGTH_SHORT).show();
            return;
        }
        if(mainCommendBean == null){
            MainCommendBean addMainCommendBean = new MainCommendBean(content);
            mAdapter.addData(0,addMainCommendBean);
            mRecyclerView.smoothScrollToPosition(0);
        }else{
            int index = mAdapter.getData().indexOf(mainCommendBean) + 1;
            ChildCommendBean childCommendBean = new ChildCommendBean(content);
            mAdapter.addData(index,childCommendBean);
        }
        mTextView.setText("");
    }

    @Override
    public void onClick(View view) {
        int id = view.getId();
        if(id == R.id.mTextView){
            showInputDialog(null);
        }else if(id == R.id.mTvSend){
            onCommitCommend(null);
        }
    }

    @Override
    public void onItemClick(MainCommendBean mainCommendBean) {
        showInputDialog(mainCommendBean);
    }
}

样式:CustomBottomSheetDialogTheme

<style name="CustomBottomSheetDialogTheme" parent="Theme.Design.BottomSheetDialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:background">@android:color/transparent</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="backgroundColor">@android:color/transparent</item>
        <item name="android:backgroundDimEnabled">false</item>
    </style>

布局:fragment_commend_msg_dialog

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom"
    android:background="@drawable/round_top_12dp_wirth_bg"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".commendDialog.dialog.CommendMsgDialogFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/mRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        app:behavior_hideable="true"
        app:behavior_peekHeight="66dp"
        android:paddingBottom="50dp"
        app:layout_behavior="@string/bottom_sheet_behavior"/>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@drawable/storke_line_1dp_dcdcdc"
        android:layout_gravity="bottom"
        app:layout_constraintBottom_toBottomOf="parent">

        <TextView
            android:id="@+id/mTvSend"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="15dp"
            android:background="@drawable/send_commend_bg"
            android:paddingLeft="15dp"
            android:paddingTop="5dp"
            android:paddingRight="15dp"
            android:paddingBottom="5dp"
            android:text="发送"
            android:textColor="@android:color/white"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/mTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="35dp"
            android:background="@drawable/et_search_bg"
            android:layout_toLeftOf="@id/mTvSend"
            android:layout_marginRight="15dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="15dp"
            android:text=""
            android:gravity="center_vertical"
            android:hint="发个评论,说不定能火"
            android:paddingTop="3dp"
            android:paddingBottom="3dp"
            android:maxLines="2"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:textColor="@color/f5f5f5"
            android:textColorHint="@color/f5f5f5"
            android:textSize="14sp" />
    </RelativeLayout>
</FrameLayout>

评论框消失监听:OnDisMissCallBack

public interface OnDisMissCallBack {
    void onDismiss();
}

发送评论的dialog框:CommendInputDialogFragment

public class CommendInputDialogFragment extends DialogFragment {
    public static final String CONTENT = "content";
    private String mContent;
    private int mAllowableErrorHeight;
    private int mLastDiff = 0;
    public CommendInputDialogFragment() {
    }

    public static CommendInputDialogFragment newInstance(String content) {
        CommendInputDialogFragment fragment = new CommendInputDialogFragment();
        Bundle args = new Bundle();
        args.putString(CONTENT, content);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContent = getArguments().getString(CONTENT);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        Window window = getDialog().getWindow();
        super.onActivityCreated(savedInstanceState);
        getDialog().setCancelable(true);
        getDialog().setCanceledOnTouchOutside(true);
        //设置背景为透明
        window.setWindowAnimations(R.style.AnimBottom);
        window.setBackgroundDrawable(ContextCompat.getDrawable(getContext(), android.R.color.transparent));
        //去除阴影
        window.setGravity(Gravity.BOTTOM);
        WindowManager.LayoutParams layoutParams = window.getAttributes();
        layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
        layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
        layoutParams.dimAmount = 0.3f;
        window.setAttributes(layoutParams);
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE|WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
        getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                dismissDialog();
            }
        });
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_commend_input_dialog, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        TextView mTvSend = view.findViewById(R.id.mTvSend);
        EditText mEditText = view.findViewById(R.id.mEditText);
        mTvSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String content = mEditText.getText().toString();
                if (TextUtils.isEmpty(content)) {
                    Toast.makeText(App.getInstance(), "内容不能为空", Toast.LENGTH_SHORT).show();
                    return;
                }
                if (mOnDisMissCallBack != null) {
                    mOnDisMissCallBack.saveCommend();
                }
                dismissDialog();
            }
        });
        //进入,就要选中输入框
        mEditText.setFocusable(true);
        mEditText.setFocusableInTouchMode(true);
        mEditText.requestFocus();
        mEditText.postDelayed(new Runnable() {
            @Override
            public void run() {
                InputMethodManager imm = (InputMethodManager) getContext()
                        .getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
                Rect r = new Rect();
                //获取当前界面可视部分
                getActivity().getWindow().getDecorView().getRootView().getWindowVisibleDisplayFrame(r);
                //获取屏幕的高度
                int screenHeight = getActivity().getWindow().getDecorView().getRootView().getHeight();

                //此处就是用来获取键盘的高度的, 在键盘没有弹出的时候 此高度为0 键盘弹出的时候为一个正数
                mAllowableErrorHeight = screenHeight - r.bottom;
                setOnKeyBordListener();
            }
        },100);
        mEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                String content = mEditText.getText().toString();
                if(mOnDisMissCallBack != null){
                    mOnDisMissCallBack.onCommendTextChanged(content);
                }
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
        //设置已经填过的文字,以及移动光标
        mEditText.setText(mContent);
        if(!TextUtils.isEmpty(mContent) && mContent.length() > 0){
            mEditText.setSelection(mContent.length());
        }
    }

    private void setOnKeyBordListener() {
        getActivity().getWindow().getDecorView().addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                try {
                    Rect r = new Rect();
                    //获取当前界面可视部分
                    getActivity().getWindow().getDecorView().getRootView().getWindowVisibleDisplayFrame(r);
                    //获取屏幕的高度
                    int screenHeight = getActivity().getWindow().getDecorView().getRootView().getHeight();

                    //此处就是用来获取键盘的高度的, 在键盘没有弹出的时候 此高度为0 键盘弹出的时候为一个正数
                    int heightDifference = screenHeight - r.bottom;
                    if (heightDifference > mAllowableErrorHeight && mLastDiff >= 0) {
                        //开软键盘
                    } else {
                        //关闭软键盘
                        dismissDialog();
                    }
                    mLastDiff = heightDifference;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private void dismissDialog() {
        if (mOnDisMissCallBack != null) {
            mOnDisMissCallBack.onDismiss();
        }
        dismissAllowingStateLoss();
    }

    public interface OnDisMissCallBack {
        void onDismiss();
        void saveCommend();
        void onCommendTextChanged(String contend);
    }

    private OnDisMissCallBack mOnDisMissCallBack;

    public void setOnDisMissCallBack(OnDisMissCallBack mOnDisMissCallBack) {
        this.mOnDisMissCallBack = mOnDisMissCallBack;
    }
}

布局:fragment_commend_input_dialog

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".commendDialog.dialog.CommendInputDialogFragment">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@drawable/storke_line_1dp_dcdcdc"
        android:layout_gravity="bottom"
        app:layout_constraintBottom_toBottomOf="parent">

        <TextView
            android:id="@+id/mTvSend"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="发送"
            android:textSize="16sp"
            android:textColor="@android:color/white"
            android:paddingTop="5dp"
            android:paddingBottom="5dp"
            android:paddingLeft="15dp"
            android:paddingRight="15dp"
            android:layout_alignParentRight="true"
            android:layout_marginRight="15dp"
            android:layout_centerVertical="true"
            android:background="@drawable/send_commend_bg" />

        <EditText
            android:id="@+id/mEditText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="35dp"
            android:background="@drawable/et_search_bg"
            android:layout_toLeftOf="@id/mTvSend"
            android:layout_marginRight="15dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="15dp"
            android:text=""
            android:gravity="center_vertical"
            android:hint="发个评论,说不定能火"
            android:paddingTop="3dp"
            android:paddingBottom="3dp"
            android:maxLines="2"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:textColorHint="@color/f5f5f5"
            android:textColor="@color/f5f5f5"
            android:textSize="14sp" />
    </RelativeLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

数据模拟工具:DataTestUtil

public class DataTestUtil {
    Handler handler = null;
    public DataTestUtil() {
        handler = new Handler(Looper.getMainLooper());
    }

    public void getMainCommends(boolean isRefresh,int page,int pageNum,OnMainCommendCallBack callBack){
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //一定的失败机率
                Random random = new Random();
                int num = random.nextInt(10);
                if((num % 10 == 1 || num % 10 == 2 || num % 10 == 3) && !isRefresh){
                    if(callBack != null){
                        callBack.onFail();
                    }
                }else {
                    //模拟线上项目的数据返回
                    //先返回一部分的主评论,子评论也只会先返回一个
                    int start = page * pageNum;
                    List<MainCommendBean> list = new ArrayList<>();
                    for(int x = start; (x < (start + pageNum)); x++){
                        MainCommendBean mainCommendBean = new MainCommendBean("主评论"+x);
                        if(x % 10 == 3){
                            //选择一部分数据作为有子数据的主评论
                            ChildCommendBean childCommendBean = new ChildCommendBean("子评论0");
                            mainCommendBean.setChildNumber(20);
                            List<ChildCommendBean> childList = new ArrayList<>();
                            childList.add(childCommendBean);
                            mainCommendBean.setChildList(childList);
                        }
                        list.add(mainCommendBean);
                    }
                    if(callBack != null){
                        callBack.onSuccess(isRefresh,list);
                    }
                }
            }
        },500);
    }

    public void getChildCommends(int currentNum,int maxChildNum,OnChildCommendCallBack onChildCommendCallBack){
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                int start = currentNum;
                List<ChildCommendBean> list = new ArrayList<>();
                for(int x = start; (x < (currentNum + 5) && x < (maxChildNum)); x++){
                    ChildCommendBean childCommendBean = new ChildCommendBean("子评论"+x);
                    list.add(childCommendBean);
                }
                if(onChildCommendCallBack != null){
                    onChildCommendCallBack.onSuccess(list);
                }
            }
        },500);
    }
}

二级评论数据回调接口:OnChildCommendCallBack

public interface OnChildCommendCallBack {
    void onSuccess(List<ChildCommendBean> list);
}

一级评论数据回调接口:OnMainCommendCallBack

public interface OnMainCommendCallBack {
    void onSuccess(boolean isRefresh, List<MainCommendBean> list);
    void onFail();
}

一级评论JavaBean:MainCommendBean

public class MainCommendBean extends AbstractExpandableItem<MultiItemEntity> implements MultiItemEntity {
    private String content;
    private List<ChildCommendBean> childList = new ArrayList<>();
    private int childNumber = 0;

    public MainCommendBean(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public List<ChildCommendBean> getChildList() {
        return childList;
    }

    public void setChildList(List<ChildCommendBean> childList) {
        this.childList = childList;
    }

    public int getChildNumber() {
        return childNumber;
    }

    public void setChildNumber(int childNumber) {
        this.childNumber = childNumber;
    }

    @Override
    public int getLevel() {
        return 0;
    }

    @Override
    public int getItemType() {
        return CommendAdapter.MAIN_COMMEND;
    }
}

二级评论javabean:ChildCommendBean

public class ChildCommendBean implements MultiItemEntity {
    private String content;

    public ChildCommendBean(String content) {
        this.content = content;
    }


    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public int getItemType() {
        return CommendAdapter.CHILD_COMMEND;
    }
}

加载更多Javabean:MoreCommendBean

public class MoreCommendBean implements MultiItemEntity {
    public boolean isLoading = false;//有没有在加载数据,他控制加载更多条目的loading控件的展示。
    private MainCommendBean mainCommendBean;
    private boolean isZheDie = true;//是否是折叠状态
    private boolean isLoaded = false;//全部子view是否已经加载完成。
    private List<ChildCommendBean> subList = new ArrayList<>();//除了开始的时候展示的一条,后面加入的新的,都存放在这

    public List<ChildCommendBean> getSubList() {
        return subList;
    }

    public boolean isLoaded() {
        return isLoaded;
    }

    public void setLoaded(boolean loaded) {
        isLoaded = loaded;
    }

    public boolean isZheDie() {
        return isZheDie;
    }

    public void setZheDie(boolean zheDie) {
        isZheDie = zheDie;
    }

    public MainCommendBean getMainCommendBean() {
        return mainCommendBean;
    }

    public void setMainCommendBean(MainCommendBean mainCommendBean) {
        this.mainCommendBean = mainCommendBean;
    }

    @Override
    public int getItemType() {
        return CommendAdapter.MORE_COMMEND;
    }
}

评论列表适配器:CommendAdapter

public class CommendAdapter extends BaseMultiItemQuickAdapter<MultiItemEntity, BaseViewHolder> {
    public static final int MAIN_COMMEND = 0;
    public static final int CHILD_COMMEND = 1;
    public static final int MORE_COMMEND = 2;
    private OnClickLoadMoreChildCallBack onClickLoadMoreChildCallBack;
    private OnCommendItemClickCallBack onCommendItemClickCallBack;

    public CommendAdapter(List<MultiItemEntity> data) {
        super(data);
        addItemType(MAIN_COMMEND, R.layout.main_commend_layout);
        addItemType(CHILD_COMMEND, R.layout.child_commend_layout);
        addItemType(MORE_COMMEND,R.layout.more_commend_layout);
    }

    public void setOnClickLoadMoreChildCallBack(OnClickLoadMoreChildCallBack onClickLoadMoreChildCallBack) {
        this.onClickLoadMoreChildCallBack = onClickLoadMoreChildCallBack;
    }

    public void setOnCommendItemClickCallBack(OnCommendItemClickCallBack onCommendItemClickCallBack) {
        this.onCommendItemClickCallBack = onCommendItemClickCallBack;
    }

    @Override
    protected void convert(@NonNull BaseViewHolder helper, MultiItemEntity item) {
        if(helper.getItemViewType() == MAIN_COMMEND){
            MainCommendBean mainCommendBean = (MainCommendBean) item;
            TextView tvCommentContent = helper.getView(R.id.tvCommentContent);
            tvCommentContent.setText(mainCommendBean.getContent());
            helper.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if(onCommendItemClickCallBack != null){
                        onCommendItemClickCallBack.onItemClick(mainCommendBean);
                    }
                }
            });
        }else if(helper.getItemViewType() == CHILD_COMMEND){
            int position = getParentPositionInAll(helper.getAdapterPosition());
            MultiItemEntity multiItemEntity = getData().get(position);
            ChildCommendBean childCommendBean = (ChildCommendBean) item;
            TextView tvCommentContent = helper.getView(R.id.tvCommentContent);
            tvCommentContent.setText(childCommendBean.getContent());
            helper.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if(onCommendItemClickCallBack != null){
                        onCommendItemClickCallBack.onItemClick((MainCommendBean) multiItemEntity);
                    }
                }
            });
        }else if(helper.getItemViewType() == MORE_COMMEND){
            TextView tvExpandNum = helper.getView(R.id.tvExpandNum);
            ProgressBar loadingImageView = helper.getView(R.id.loadingImageView);
            MoreCommendBean moreCommendBean = (MoreCommendBean) item;
            setLoadingState(tvExpandNum,loadingImageView,moreCommendBean);

            setTipsText(tvExpandNum,moreCommendBean);
            tvExpandNum.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if(onClickLoadMoreChildCallBack != null){
                        onClickLoadMoreChildCallBack.onClickMoreChild(tvExpandNum,loadingImageView,moreCommendBean);
                    }
                }
            });
        }
    }

    /**
     * 设置折叠条目的文案
     * @param tvExpandNum
     * @param moreCommendBean
     */
    private void setTipsText(TextView tvExpandNum, MoreCommendBean moreCommendBean) {
        if(moreCommendBean.isZheDie()){
            int number = 0;
            if(!moreCommendBean.isLoaded()){
                //没有全部加载完成的话,剩余条目就是全部子条目的数目减去已经加载了的子条目数目
                number  = moreCommendBean.getMainCommendBean().getChildNumber() - moreCommendBean.getMainCommendBean().getChildList().size();
            }else{
                //全部都加在过了,那么提示的条目就应该是后续额外请求网络数据加载的子条目数
                number = moreCommendBean.getSubList().size();
            }
            tvExpandNum.setText("展开剩余"+number+"条评论");
        }else{
            tvExpandNum.setText("收起");
        }
    }

    /**
     * 设置加载状态,由于recycleview的复用,
     * 所以我们要每次加载数据,就要判断一下该条目是否在加载
     * @param tvExpandNum
     * @param loadingImageView
     * @param moreCommendBean
     */
    private void setLoadingState(TextView tvExpandNum, ProgressBar loadingImageView,MoreCommendBean moreCommendBean) {
        if(moreCommendBean.isLoading){
            tvExpandNum.setVisibility(View.GONE);
            loadingImageView.setVisibility(View.VISIBLE);
        }else{
            tvExpandNum.setVisibility(View.VISIBLE);
            loadingImageView.setVisibility(View.GONE);
        }
    }
}

一级评论布局:main_commend_layout

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    android:layout_marginBottom="7dp"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">


    <ImageView
        android:id="@+id/imgAvatar"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:layout_marginStart="12dp"
        android:src="@mipmap/ic_launcher"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tvCommentNickname"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:tag="binding_3"
        android:text="小猪乔治"
        android:textColor="@color/ff333333"
        android:textSize="12sp"
        app:layout_constraintLeft_toRightOf="@+id/imgAvatar"
        app:layout_constraintTop_toTopOf="@+id/imgAvatar" />

    <TextView
        android:id="@+id/tvCommentTagAuthor"
        android:layout_width="28dp"
        android:layout_height="14dp"
        android:layout_marginLeft="2dp"
        android:layout_marginTop="2dp"
        android:layout_marginBottom="3dp"
        android:background="@drawable/community_focus_shape_orange_2"
        android:gravity="center"
        android:tag="binding_4"
        android:text="@string/author"
        android:textColor="@color/mainColor"
        android:textSize="10sp"
        app:layout_constraintLeft_toRightOf="@+id/tvCommentNickname"
        app:layout_constraintTop_toTopOf="@+id/tvCommentNickname" />

    <TextView
        android:id="@+id/tvTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="4dp"
        android:text="2021-05-24"
        android:textColor="@color/ccccccc"
        android:textSize="11sp"
        app:layout_constraintBottom_toBottomOf="@+id/tvCommentNickname"
        app:layout_constraintLeft_toRightOf="@+id/tvCommentTagAuthor"
        app:layout_constraintTop_toTopOf="@+id/tvCommentNickname" />

    <ImageView
        android:id="@+id/imageLike"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        android:paddingEnd="12dp"
        android:paddingStart="12dp"
        app:layout_constraintTop_toTopOf="@+id/tvCommentNickname"
        android:src="@mipmap/community_found_icon_comment_line_love"/>
    <TextView
        android:id="@+id/tvCommentLike"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        tools:text="3456"
        android:textColor="@color/ff666666"
        android:textSize="11sp"
        app:layout_constraintStart_toStartOf="@+id/imageLike"
        app:layout_constraintEnd_toEndOf="@+id/imageLike"
        app:layout_constraintTop_toBottomOf="@+id/imageLike" />

    <TextView
        android:id="@+id/tvCommentContent"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="5dp"
        android:layout_marginEnd="56dp"
        tools:text="我觉得小饼干简直是人间美味啊~"
        android:textColor="@color/ff666666"
        android:textSize="13sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imgAvatar"
        app:layout_constraintTop_toBottomOf="@+id/tvCommentNickname" />
</androidx.constraintlayout.widget.ConstraintLayout>

二级评论布局:child_commend_layout

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    android:layout_marginBottom="7dp"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <ImageView
        android:id="@+id/imgAvatar"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginStart="56dp"
        android:src="@mipmap/ic_launcher"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <TextView
        android:id="@+id/tvCommentNickname"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:tag="binding_3"
        android:text="小猪乔治"
        android:textColor="@color/ff333333"
        android:textSize="12sp"
        app:layout_constraintLeft_toRightOf="@+id/imgAvatar"
        app:layout_constraintTop_toTopOf="@+id/imgAvatar" />

    <TextView
        android:id="@+id/tvCommentTagAuthor"
        android:layout_width="28dp"
        android:layout_height="14dp"
        android:layout_marginLeft="2dp"
        android:layout_marginTop="2dp"
        android:layout_marginBottom="3dp"
        android:background="@drawable/community_focus_shape_orange_2"
        android:gravity="center"
        android:tag="binding_4"
        android:text="@string/author"
        android:textColor="@color/mainColor"
        android:textSize="10sp"
        app:layout_constraintLeft_toRightOf="@+id/tvCommentNickname"
        app:layout_constraintTop_toTopOf="@+id/tvCommentNickname" />

    <TextView
        android:id="@+id/tvTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/dp_4"
        tools:text="2021-05-24"
        android:textColor="@color/ccccccc"
        android:textSize="11sp"
        app:layout_constraintBottom_toBottomOf="@+id/tvCommentNickname"
        app:layout_constraintLeft_toRightOf="@+id/tvCommentTagAuthor"
        app:layout_constraintTop_toTopOf="@+id/tvCommentNickname" />


    <ImageView
        android:id="@+id/imageLike"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        android:paddingEnd="12dp"
        android:paddingStart="12dp"
        app:layout_constraintTop_toTopOf="@+id/tvCommentNickname"
        android:src="@mipmap/community_found_icon_comment_line_love"/>
    <TextView
        android:id="@+id/tvCommentLike"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        tools:text="3456"
        android:textColor="@color/ff666666"
        android:textSize="11sp"
        app:layout_constraintStart_toStartOf="@+id/imageLike"
        app:layout_constraintEnd_toEndOf="@+id/imageLike"
        app:layout_constraintTop_toBottomOf="@+id/imageLike" />

    <TextView
        android:id="@+id/tvCommentContent"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_marginEnd="56dp"
        tools:text="我觉得小饼干简直是人间美味啊~"
        android:textColor="@color/ff666666"
        android:textSize="13sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/tvCommentNickname"
        app:layout_constraintTop_toBottomOf="@+id/tvCommentNickname" />

</androidx.constraintlayout.widget.ConstraintLayout>

加载更多布局:more_commend_layout

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <TextView
        android:id="@+id/tvExpandNum"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="11sp"
        android:layout_marginStart="56dp"
        android:textColor="@color/ff333333"
        android:paddingTop="6dp"
        android:paddingBottom="7dp"
        android:drawablePadding="@dimen/dp_4"
        android:visibility="gone"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:drawableEnd="@mipmap/community_icon_common_down_white"/>
    <ProgressBar
        android:id="@+id/loadingImageView"
        android:layout_width="25dp"
        android:layout_height="25dp"
        app:layout_constraintStart_toStartOf="parent"
        android:visibility="gone"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="11dp"
        app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

加载更多点击回调:OnClickLoadMoreChildCallBack

public interface OnClickLoadMoreChildCallBack {
    void onClickMoreChild(TextView tvExpandNum, ProgressBar loadingImageView, MoreCommendBean moreCommendBean);
}

一级、二级评论被点击时回调:OnCommendItemClickCallBack

public interface OnCommendItemClickCallBack {
    void onItemClick(MainCommendBean mainCommendBean);
}

然后在activity里面点击一个按钮弹出对话框

class CommendDialogActivity : AppCompatActivity() {
    private var mCommendMsgDialogFragment:CommendMsgDialogFragment? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_commend_dialog)
        mBtnCommend?.setOnClickListener {
            if (mCommendMsgDialogFragment == null) {
                 mCommendMsgDialogFragment = CommendMsgDialogFragment.newInstance()
                mCommendMsgDialogFragment?.setOnDisMissCallBack { mCommendMsgDialogFragment = null }
                mCommendMsgDialogFragment?.show(supportFragmentManager,mCommendMsgDialogFragment?.tag)
            }
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容