【Android】Jetpack全组件实战开发短视频应用App(二十)

前言

项目地址.
我们这一篇主要是完成添加评论和删除评论,我们先看下效果

实现

我们首先看下添加评论,点击输入布局底部弹出一个对话框
【Android】Jetpack全组件实战开发短视频应用App(二十)_第1张图片
它有4部分组成:1.可以使一张图片或者一个视频;2.输入框;3.拍摄视频;4.发送按钮


<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

    data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:animateLayoutChanges="false"
        android:background="@color/color_white"
        android:orientation="vertical">

        <FrameLayout
            android:id="@+id/comment_ext_layout"
            android:layout_width="@dimen/dp_60"
            android:layout_height="@dimen/dp_60"
            android:layout_marginLeft="@dimen/dimen_10"
            android:layout_marginTop="@dimen/dp_5"
            android:layout_marginBottom="@dimen/dp_5"
            android:visibility="gone"
            tools:visibility="visible">

            <com.mooc.ppjoke.view.PPImageView
                android:id="@+id/comment_cover"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop">com.mooc.ppjoke.view.PPImageView>

            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/comment_icon_video"
                android:layout_width="@dimen/dimen_20"
                android:layout_height="@dimen/dimen_20"
                android:layout_gravity="center"
                android:src="@drawable/icon_video_play"
                android:visibility="gone">androidx.appcompat.widget.AppCompatImageView>

            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/comment_delete"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="right|top"
                android:paddingLeft="@dimen/dp_3"
                app:srcCompat="@drawable/icon_close"
                app:tint="@color/color_white">androidx.appcompat.widget.AppCompatImageView>
        FrameLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:background="@color/color_gray2">View>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_45"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:paddingLeft="@dimen/dimen_10"
            android:paddingRight="@dimen/dimen_10">

            <com.mooc.libcommon.view.PPEditTextView
                android:id="@+id/input_view"
                android:layout_width="0dp"
                android:layout_height="35dp"
                android:layout_weight="1"
                android:background="@drawable/bg_edit_view"
                android:gravity="center_vertical"
                android:hint="@string/comment_hint"
                android:paddingLeft="@dimen/sp_10"
                android:textColor="@color/color_333"
                android:textColorHint="@color/color_999"
                android:textSize="@dimen/sp_12">com.mooc.libcommon.view.PPEditTextView>

            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/comment_video"
                android:layout_width="@dimen/dp_36"
                android:layout_height="@dimen/dp_36"
                android:layout_marginLeft="@dimen/dp_5"
                android:src="@drawable/icon_publish_video">androidx.appcompat.widget.AppCompatImageView>

            <com.google.android.material.button.MaterialButton
                android:id="@+id/comment_send"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/dimen_10"
                android:gravity="center"
                android:paddingLeft="@dimen/dp_11"
                android:paddingTop="@dimen/dp_3"
                android:paddingRight="@dimen/dp_11"
                android:paddingBottom="@dimen/dp_3"
                android:text="@string/comment_send_text"
                android:textColorHint="@color/color_white"
                app:backgroundTint="@color/color_theme"
                app:cornerRadius="@dimen/dp_6">com.google.android.material.button.MaterialButton>
        LinearLayout>
    LinearLayout>
layout>

我们这个底部对话框使用DialogFragment实现

public class CommentDialog extends AppCompatDialogFragment

添加评论肯定需要整条帖子的id,所以需要给我们的CommentDialog传一个值

   public static CommentDialog newInstance(long itemId) {

        Bundle args = new Bundle();
        args.putLong(KEY_ITEM_ID, itemId);
        CommentDialog fragment = new CommentDialog();
        fragment.setArguments(args);
        return fragment;
    }

拍摄视频我们下一篇再说,我们这一篇主要就是添加和删除评论,我们为这个发送写一个点击事件

 mBinding.commentSend.setOnClickListener(this);
 
  @Override
    public void onClick(View v) {
        if (v.getId() == R.id.comment_send) {
            publishComment();
        } 

    }
    private void publishComment() {

        if (TextUtils.isEmpty(mBinding.inputView.getText())) {
            return;
        }
        String commentText = mBinding.inputView.getText().toString();
        ApiService.post("/comment/addComment")
                .addParam("userId", UserManager.get().getUserId())
                .addParam("itemId", itemId)
                .addParam("commentText", commentText)
                .addParam("image_url", isVideo ? coverUrl : fileUrl)
                .addParam("video_url", isVideo ? fileUrl : null)
                .addParam("width", width)
                .addParam("height", height)
                .execute(new JsonCallback<Comment>() {
                    @Override
                    public void onSuccess(ApiResponse<Comment> response) {
                        onCommentSuccess(response.body);
                        dismissLoadingDialog();
                    }

                    @Override
                    public void onError(ApiResponse<Comment> response) {
                        showToast("评论失败:" + response.message);
                        dismissLoadingDialog();
                    }
                });
    }
    
  private void onCommentSuccess(Comment body) {
        showToast("评论发布成功");
        ArchTaskExecutor.getMainThreadExecutor().execute(() -> {
            if (mListener != null) {
                mListener.onAddComment(body);
            }
            dismiss();
        });
    }

我们评论评论成功之后需要把值回调给外面,所以这里写了一个接口

private OnCommentAddListener mListener;

 public interface OnCommentAddListener {
        void onAddComment(Comment comment);
    }

    public void setCommentAddListener(OnCommentAddListener listener) {

        mListener = listener;
    }

Ok,接着我们在需要弹出这个对话框的地方补上我们的点击事件,在我们的ViewHandler这个类中添加对输入View的点击事件

  mInateractionBinding.inputView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showCommentDialog();
            }
        });
        
 private void showCommentDialog() {
        if (commentDialog == null) {
            commentDialog = CommentDialog.newInstance(mFeed.itemId);
        }
        commentDialog.setCommentAddListener(comment -> {
            handleEmpty(true);
            listAdapter.addAndRefreshList(comment);
        });
        commentDialog.show(mActivity.getSupportFragmentManager(), "comment_dialog");
    }

我们主要是看下handleEmptyaddAndRefreshList这两个方法
handleEmpty是我们在没有评论的时候需要展示空布局,有数据时不展示这个空布局

    public void handleEmpty(boolean hasData) {
        if (hasData) {
            if (mEmptyView != null) {
                listAdapter.removeHeaderView(mEmptyView);
            }
        } else {
            if (mEmptyView == null) {
                mEmptyView = new EmptyView(mActivity);
                RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                layoutParams.topMargin = PixUtils.dp2px(40);
                mEmptyView.setLayoutParams(layoutParams);
                mEmptyView.setTitle(mActivity.getString(R.string.feed_comment_empty));
            }
            listAdapter.addHeaderView(mEmptyView);
        }
    }

主要是这个addAndRefreshList方法,我们每次添加完一条评论,这条评论就会在最上面,同时我们这个评论是可以上拉加载的,我们在首页帖子列表那里处理过上拉加载和下拉刷新,思路和那个差不多,就是上拉的时候,我们重新构建PageList,然后调用submitList方法刷新数据.

public void addAndRefreshList(Comment comment) {
        PagedList<Comment> currentList = getCurrentList();
        MutableItemKeyedDataSource<Integer, Comment> mutableItemKeyedDataSource = new MutableItemKeyedDataSource<Integer, Comment>((ItemKeyedDataSource) currentList.getDataSource()) {
            @NonNull
            @Override
            public Integer getKey(@NonNull Comment item) {
                return item.id;
            }
        };
        mutableItemKeyedDataSource.data.add(comment);
        mutableItemKeyedDataSource.data.addAll(currentList);
        PagedList<Comment> pagedList = mutableItemKeyedDataSource.buildNewPagedList(currentList.getConfig());
        submitList(pagedList);
    }

这样基本上添加评论就写完了,然后我们就开始写删除评论,这个稍微简单点,就是调用相应接口就行

  //删除某个帖子的一个评论
    public static LiveData<Boolean> deleteFeedComment(Context context, long itemId, long commentId) {
        MutableLiveData<Boolean> liveData = new MutableLiveData<>();
        new AlertDialog.Builder(context)
                .setNegativeButton("删除", (dialog, which) -> {
                    dialog.dismiss();
                    deleteFeedCommentInternal(liveData, itemId, commentId);
                }).setPositiveButton("取消", (dialog, which) -> dialog.dismiss()).setMessage("确定要删除这条评论吗?").create().show();
        return liveData;
    }

    private static void deleteFeedCommentInternal(LiveData liveData, long itemId, long commentId) {
        ApiService.get("/comment/deleteComment")
                .addParam("userId", UserManager.get().getUserId())
                .addParam("commentId", commentId)
                .addParam("itemId", itemId)
                .execute(new JsonCallback<JSONObject>() {
                    @Override
                    public void onSuccess(ApiResponse<JSONObject> response) {
                        if (response.body != null) {
                            boolean result = response.body.getBooleanValue("result");
                            ((MutableLiveData) liveData).postValue(result);
                            showToast("评论删除成功");
                        }
                    }

                    @Override
                    public void onError(ApiResponse<JSONObject> response) {
                        showToast(response.message);
                    }
                });
    }

这个点击事件在FeedCommentAdapter中,只有自己的评论我们才能删除

  holder.mBinding.commentDelete.setOnClickListener(v ->
                InteractionPresenter.deleteFeedComment(mContext, item.itemId, item.commentId)
                        .observe((LifecycleOwner) mContext, success -> {
                            if (success) {
                                deleteAndRefreshList(item);
                            }
                        }));
    public void deleteAndRefreshList(Comment item) {
        MutableItemKeyedDataSource<Integer, Comment> dataSource = new MutableItemKeyedDataSource<Integer, Comment>((ItemKeyedDataSource) getCurrentList().getDataSource()) {
            @NonNull
            @Override
            public Integer getKey(@NonNull Comment item) {
                return item.id;
            }
        };
        PagedList<Comment> currentList = getCurrentList();
        for (Comment comment : currentList) {
            if (comment != item) {
                dataSource.data.add(comment);
            }
        }
        PagedList<Comment> pagedList = dataSource.buildNewPagedList(getCurrentList().getConfig());
        submitList(pagedList);
    }

删除成功之后,和添加正好相反,我们需要把我们的这条评论从列表中删除

你可能感兴趣的:(Jetpack)