Android仿抖音评论列表

龙旋

共 26861字,需浏览 54分钟

 ·

2021-09-01 12:08

效果图:



BottomSheetDialogFragment是可以上下滑动退出的dialogFragment,里面就是behavior起到了作用,感兴趣的可以去了解了解,我这里记录一下写过的demo。仔细观察抖音的评论列表,它底部的请输入框其实是个Textview,点击后才弹出输入的dialog弹窗,如果不这样做,会有一个问题,就是列表往下滑的时候,底部的布局并没有跟着移动。当然,我们也能做一个recycleview的底部条目为输入框,这里我采取了点击弹出真正的对话框的方式。好,下面直接上代码。


评论列表

列表fragment

public class CommendMsgDialogFragment extends BottomSheetDialogFragment {
private RecyclerView mRecyclerView; private CommendAdapter mCommendAdapter; private FragmentCommendMsgDialogBinding mBinding; private String mUniquekey; private CommendInputDialogFragment mCommendInputDialog;
public CommendMsgDialogFragment() { // Required empty public constructor }
public static CommendMsgDialogFragment newInstance(String uniquekey) { CommendMsgDialogFragment fragment = new CommendMsgDialogFragment(); Bundle args = new Bundle(); args.putString("uniquekey",uniquekey); fragment.setArguments(args); return fragment; }
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //设置背景透明,才能显示出layout中诸如圆角的布局,否则会有白色底(框) setStyle(BottomSheetDialogFragment.STYLE_NORMAL, R.style.CustomBottomSheetDialogTheme); if (getArguments() != null) { mUniquekey = getArguments().getString("uniquekey"); mUniquekey = "c322b12d56a3125e73e7b2978ff846c0"; } }
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment mBinding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.fragment_commend_msg_dialog, container, false); return mBinding.getRoot(); }
@Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { Window window = getDialog().getWindow(); window.requestFeature(Window.FEATURE_NO_TITLE); super.onActivityCreated(savedInstanceState); //设置背景为透明 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.dimAmount = 0.1f; window.setAttributes(layoutParams); getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { if(mOnDisMissCallBack != null){ mOnDisMissCallBack.onDismiss(); } dismissAllowingStateLoss(); } }); }
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mRecyclerView = mBinding.mBottomSheet; mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); mCommendAdapter = new CommendAdapter(); mRecyclerView.setAdapter(mCommendAdapter); mBinding.mTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showInputDialog(); } }); mBinding.mTvSend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { savePost(); } }); initData(); }
private void showInputDialog() { if(mCommendInputDialog == null){ mCommendInputDialog = CommendInputDialogFragment.newInstance(mUniquekey,mBinding.mTextView.getText().toString()); mCommendInputDialog.setOnDisMissCallBack(new CommendInputDialogFragment.OnDisMissCallBack() { @Override public void onDismiss() { mCommendInputDialog = null; }
@Override public void saveCommend() { savePost(); }
@Override public void onCommendTextChanged(String contend) { mBinding.mTextView.setText(contend); } }); mCommendInputDialog.show(getChildFragmentManager(),mCommendInputDialog.getTag()); } }
/** * 根据key值获取他的所有评论,按时间排序 */ private void initData() { if (BmobUser.isLogin()) { BmobQuery<CommentBean> query = new BmobQuery<>(); query.addWhereEqualTo("uniquekey", mUniquekey); query.order("-updatedAt"); //包含作者信息 query.include("user"); query.findObjects(new FindListener<CommentBean>() { @Override public void done(List<CommentBean> object, BmobException e) { if (e == null) { mCommendAdapter.refreshList(object); } else { Toast.makeText(App.getInstance(), "请求失败:"+e.getMessage(), Toast.LENGTH_SHORT).show(); } }
}); } else { Toast.makeText(App.getInstance(), "请先登录~", Toast.LENGTH_SHORT).show(); } }
/** * 添加一对一关联,当前用户发布帖子 */ private void savePost() { String content = mBinding.mTextView.getText().toString(); if (TextUtils.isEmpty(content)) { Toast.makeText(App.getInstance(), "内容不能为空", Toast.LENGTH_SHORT).show(); return; } if (BmobUser.isLogin()) { CommentBean post = new CommentBean(); post.setContent(content); post.setUniquekey(mUniquekey); //添加一对一关联,用户关联帖子 post.setUser(BmobUser.getCurrentUser(User.class)); post.save(new SaveListener<String>() { @Override public void done(String s, BmobException e) { if (e == null) { Toast.makeText(App.getInstance(), "评论成功~", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(App.getInstance(), "评论失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } } }); } else { Toast.makeText(App.getInstance(), "请先登录~", Toast.LENGTH_SHORT).show(); } }
public interface OnDisMissCallBack{ void onDismiss(); } private OnDisMissCallBack mOnDisMissCallBack;
public void setOnDisMissCallBack(OnDisMissCallBack mOnDisMissCallBack) { this.mOnDisMissCallBack = mOnDisMissCallBack; }}


这里的initData方法是数据源,我这里用的是bmbo第三方sdk,他可以把数据存到他们的数据库中,并且可以用sql代码查询,少量的数据是免费的,适合学生党来写毕业论文。然后是布局:

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:fitsSystemWindows="true">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" tools:context=".fragments.dialogfragments.CommendMsgDialogFragment">
<androidx.recyclerview.widget.RecyclerView android:id="@+id/mBottomSheet" android:layout_width="match_parent" android:layout_height="400dp" android:orientation="vertical" app:behavior_hideable="true" app:behavior_peekHeight="66dp" android:paddingBottom="50dp" app:layout_behavior="@string/bottom_sheet_behavior" android:background="@drawable/bg_round15_top_white" app:layout_constraintTop_toTopOf="parent"></androidx.recyclerview.widget.RecyclerView> <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="@color/write" 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" />
<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/ff333333" android:textSize="14sp" /> </RelativeLayout> </androidx.constraintlayout.widget.ConstraintLayout></layout>


adapter代码

public class CommendAdapter extends RecyclerView.Adapter<CommendAdapter.ViewHolder> {    private List<CommentBean> list = new ArrayList<>();    @NonNull    @Override    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.commend_item_layout, parent, false);        CommendItemLayoutBinding bind = DataBindingUtil.bind(view);        return new ViewHolder(bind);    }
@Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { CommentBean commentBean = list.get(position); if(commentBean != null){ if(commentBean.getUser().getAvatar() != null){ ImageLoader.getInstance().loadCircleImage(holder.mBinding.mIvHead.getContext(), commentBean.getUser().getAvatar().getUrl(),R.mipmap.default_head_img,holder.mBinding.mIvHead); } holder.mBinding.mTvNickName.setText(commentBean.getUser().getNickname()); holder.mBinding.mTvContent.setText(commentBean.getContent()); } }
@Override public int getItemCount() { return list.size(); }
public void refreshList(List<CommentBean> object) { this.list.clear(); this.list.addAll(object); notifyDataSetChanged(); }

public class ViewHolder extends RecyclerView.ViewHolder { CommendItemLayoutBinding mBinding; public ViewHolder(@NonNull View itemView) { super(itemView); } public ViewHolder(@NonNull CommendItemLayoutBinding bind) { super(bind.getRoot()); mBinding = bind; } }}


然后是条目布局

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="10dp" android:paddingRight="10dp" android:paddingTop="10dp" android:orientation="vertical">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal">
<ImageView android:id="@+id/mIvHead" android:layout_width="50dp" android:layout_height="50dp" android:src="@mipmap/default_head_img" />
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:orientation="vertical">
<TextView android:id="@+id/mTvNickName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="昵称" android:textColor="@color/ff333333" android:textSize="18sp" />
<TextView android:id="@+id/mTvContent" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" android:textColor="@color/ff949494" android:textSize="14sp" android:layout_marginTop="5dp" /> </LinearLayout> </LinearLayout>
<View android:layout_width="match_parent" android:layout_height="0.5dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:background="@color/f5f5f5" /> </LinearLayout></layout>


很简单的布局
好当我们点击底部的输入框时,弹出真正的输入弹窗。

mBinding.mTextView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                showInputDialog();            }        });
private void showInputDialog() {        if(mCommendInputDialog == null){            mCommendInputDialog = CommendInputDialogFragment.newInstance(mUniquekey,mBinding.mTextView.getText().toString());            mCommendInputDialog.setOnDisMissCallBack(new CommendInputDialogFragment.OnDisMissCallBack() {                @Override                public void onDismiss() {                    mCommendInputDialog = null;                }
@Override public void saveCommend() { savePost(); }
@Override public void onCommendTextChanged(String contend) { mBinding.mTextView.setText(contend); } }); mCommendInputDialog.show(getChildFragmentManager(),mCommendInputDialog.getTag()); } }


接下来上输入框弹窗的代码

输入框

public class CommendInputDialogFragment extends DialogFragment {
private FragmentCommendInputDialogBinding mBinding; private int mAllowableErrorHeight;
public CommendInputDialogFragment() { // Required empty public constructor }
private String mUniquekey; private String mContent; private int mLastDiff = 0; public static final String UNIQUEKEY = "uniquekey"; public static final String CONTENT = "content";
public static CommendInputDialogFragment newInstance(String uniquekey,String content) { CommendInputDialogFragment fragment = new CommendInputDialogFragment(); Bundle args = new Bundle(); args.putString(UNIQUEKEY, uniquekey); args.putString(CONTENT, content); fragment.setArguments(args); return fragment; }
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mUniquekey = getArguments().getString(UNIQUEKEY); mUniquekey = "c322b12d56a3125e73e7b2978ff846c0"; 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.5f; 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 mBinding = DataBindingUtil.bind(inflater.inflate(R.layout.fragment_commend_input_dialog, container, false)); return mBinding.getRoot(); }
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mBinding.mTvSend.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String content = mBinding.mEditText.getText().toString(); if (TextUtils.isEmpty(content)) { Toast.makeText(App.getInstance(), "内容不能为空", Toast.LENGTH_SHORT).show(); return; } if (mOnDisMissCallBack != null) { mOnDisMissCallBack.saveCommend(); } dismissDialog(); } }); mBinding.mEditText.setFocusable(true); mBinding.mEditText.setFocusableInTouchMode(true); mBinding.mEditText.requestFocus(); mBinding.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); mBinding.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 = mBinding.mEditText.getText().toString(); if(mOnDisMissCallBack != null){ mOnDisMissCallBack.onCommendTextChanged(content); } }
@Override public void afterTextChanged(Editable s) {
} }); //设置已经填过的文字,以及移动光标 mBinding.mEditText.setText(mContent); if(!TextUtils.isEmpty(mContent) && mContent.length() > 0){ mBinding.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(); } } });// mBinding.mRoot.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {// @Override// public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {// Rect r = new Rect();// //获取当前界面可视部分// getActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(r);// //获取屏幕的高度// int screenHeight = getActivity().getWindow().getDecorView().getRootView().getHeight();//// //此处就是用来获取键盘的高度的, 在键盘没有弹出的时候 此高度为0 键盘弹出的时候为一个正数// int heightDifference = screenHeight - r.bottom;// if (heightDifference <= mAllowableErrorHeight && mLastDiff >= 0) {// //开软键盘// } else {// //关闭软键盘// dismissDialog();// }// Log.d("yanjin","heightDifference = "+heightDifference+" mLastDiff="+mLastDiff);// mLastDiff = heightDifference;// }// }); }
private void dismissDialog() { if (mOnDisMissCallBack != null) { mOnDisMissCallBack.onDismiss(); } dismissAllowingStateLoss(); }
@Override public void dismiss() { super.dismiss(); mLastDiff = 0; }
public interface OnDisMissCallBack { void onDismiss(); void saveCommend(); void onCommendTextChanged(String contend); }
private OnDisMissCallBack mOnDisMissCallBack;
public void setOnDisMissCallBack(OnDisMissCallBack mOnDisMissCallBack) { this.mOnDisMissCallBack = mOnDisMissCallBack; }}


布局

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<FrameLayout android:id="@+id/mRoot" android:layout_width="match_parent" android:layout_height="wrap_content" tools:context=".adapter.CommendInputDialogFragment">
<RelativeLayout android:id="@+id/rldlgview" android:layout_width="match_parent" android:layout_height="50dp" android:background="@drawable/storke_line_1dp_dcdcdc">
<TextView android:id="@+id/mTvSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="发送" android:textSize="16sp" android:textColor="@color/write" 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:hint="发个评论,说不定能火" android:paddingTop="3dp" android:paddingBottom="3dp" android:maxLines="2" android:gravity="center_vertical" android:paddingLeft="10dp" android:paddingRight="10dp" android:textColor="@color/ff333333" android:textSize="14sp" /> </RelativeLayout>
</FrameLayout></layout>


仔细观察抖音,他是点击弹窗外部,隐藏dialog和软键盘、点击物理返回键隐藏dialog、点击软键盘的收起按钮隐藏dialog。这三点很重要

//解决点击弹窗外部,隐藏dialog和软键盘getDialog().setCanceledOnTouchOutside(true);


而点击物理返回键隐藏dialog、点击软键盘的收起按钮隐藏dialog这两个其实都只要监听软键盘隐藏就可以了,那怎么做呢?看下面的代码。

private void setOnKeyBordListener() {        mBinding.rldlgview.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {            @Override            public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {                Rect r = new Rect();                //获取当前界面可视部分                getActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(r);                //获取屏幕的高度                int screenHeight = getActivity().getWindow().getDecorView().getRootView().getHeight();                //此处就是用来获取键盘的高度的, 在键盘没有弹出的时候 此高度为0 键盘弹出的时候为一个正数                int heightDifference = screenHeight - r.bottom;
if (heightDifference <= 0 && mLastDiff >= 0) { //开软键盘 } else { //关闭软键盘 dismissDialog(); } mLastDiff = heightDifference; } }); }


好,写完收工。
哦,对了怎么调起弹窗呢?看如下代码即可:

if(mCommendMsgDialogFragment == null){                mCommendMsgDialogFragment = CommendMsgDialogFragment.newInstance(uniquekey);                mCommendMsgDialogFragment.setOnDisMissCallBack(new CommendMsgDialogFragment.OnDisMissCallBack() {                    @Override                    public void onDismiss() {                        mCommendMsgDialogFragment = null;                    }                });                mCommendMsgDialogFragment.show(getSupportFragmentManager(), mCommendMsgDialogFragment.getTag());            }


到这里就结束啦。

浏览 24
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报