Android仿抖音評論列表
效果圖:

BottomSheetDialogFragment是可以上下滑動退出的dialogFragment,里面就是behavior起到了作用,感興趣的可以去了解了解,我這里記錄一下寫過的demo。仔細(xì)觀察抖音的評論列表,它底部的請輸入框其實是個Textview,點擊后才彈出輸入的dialog彈窗,如果不這樣做,會有一個問題,就是列表往下滑的時候,底部的布局并沒有跟著移動。當(dāng)然,我們也能做一個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;}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//設(shè)置背景透明,才能顯示出layout中諸如圓角的布局,否則會有白色底(框)setStyle(BottomSheetDialogFragment.STYLE_NORMAL, R.style.CustomBottomSheetDialogTheme);if (getArguments() != null) {mUniquekey = getArguments().getString("uniquekey");mUniquekey = "c322b12d56a3125e73e7b2978ff846c0";}}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentmBinding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.fragment_commend_msg_dialog, container, false);return mBinding.getRoot();}@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {Window window = getDialog().getWindow();window.requestFeature(Window.FEATURE_NO_TITLE);super.onActivityCreated(savedInstanceState);//設(shè)置背景為透明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() {@Overridepublic void onDismiss(DialogInterface dialog) {if(mOnDisMissCallBack != null){mOnDisMissCallBack.onDismiss();}dismissAllowingStateLoss();}});}@Overridepublic 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() {@Overridepublic void onClick(View v) {showInputDialog();}});mBinding.mTvSend.setOnClickListener(new View.OnClickListener() {@Overridepublic 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() {@Overridepublic void onDismiss() {mCommendInputDialog = null;}@Overridepublic void saveCommend() {savePost();}@Overridepublic void onCommendTextChanged(String contend) {mBinding.mTextView.setText(contend);}});mCommendInputDialog.show(getChildFragmentManager(),mCommendInputDialog.getTag());}}/*** 根據(jù)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>() {@Overridepublic 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();}}/*** 添加一對一關(guān)聯(lián),當(dāng)前用戶發(fā)布帖子*/private void savePost() {String content = mBinding.mTextView.getText().toString();if (TextUtils.isEmpty(content)) {Toast.makeText(App.getInstance(), "內(nèi)容不能為空", Toast.LENGTH_SHORT).show();return;}if (BmobUser.isLogin()) {CommentBean post = new CommentBean();post.setContent(content);post.setUniquekey(mUniquekey);//添加一對一關(guān)聯(lián),用戶關(guān)聯(lián)帖子post.setUser(BmobUser.getCurrentUser(User.class));post.save(new SaveListener<String>() {@Overridepublic 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方法是數(shù)據(jù)源,我這里用的是bmbo第三方sdk,他可以把數(shù)據(jù)存到他們的數(shù)據(jù)庫中,并且可以用sql代碼查詢,少量的數(shù)據(jù)是免費的,適合學(xué)生黨來寫畢業(yè)論文。然后是布局:
<?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.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"tools:context=".fragments.dialogfragments.CommendMsgDialogFragment"><androidx.recyclerview.widget.RecyclerViewandroid: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><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="50dp"android:background="@drawable/storke_line_1dp_dcdcdc"android:layout_gravity="bottom"app:layout_constraintBottom_toBottomOf="parent"><TextViewandroid:id="@+id/mTvSend"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="發(fā)送"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" /><TextViewandroid: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="發(fā)個評論,說不定能火"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@Overridepublic 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);}@Overridepublic 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());}}@Overridepublic 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><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:paddingLeft="10dp"android:paddingRight="10dp"android:paddingTop="10dp"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><ImageViewandroid:id="@+id/mIvHead"android:layout_width="50dp"android:layout_height="50dp"android:src="@mipmap/default_head_img" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="5dp"android:orientation="vertical"><TextViewandroid:id="@+id/mTvNickName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="昵稱"android:textColor="@color/ff333333"android:textSize="18sp" /><TextViewandroid: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><Viewandroid:layout_width="match_parent"android:layout_height="0.5dp"android:layout_marginTop="10dp"android:layout_marginBottom="10dp"android:background="@color/f5f5f5" /></LinearLayout></layout>
很簡單的布局
好當(dāng)我們點擊底部的輸入框時,彈出真正的輸入彈窗。
mBinding.mTextView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {showInputDialog();}});
private void showInputDialog() {if(mCommendInputDialog == null){mCommendInputDialog = CommendInputDialogFragment.newInstance(mUniquekey,mBinding.mTextView.getText().toString());mCommendInputDialog.setOnDisMissCallBack(new CommendInputDialogFragment.OnDisMissCallBack() {@Overridepublic void onDismiss() {mCommendInputDialog = null;}@Overridepublic void saveCommend() {savePost();}@Overridepublic 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;}public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (getArguments() != null) {mUniquekey = getArguments().getString(UNIQUEKEY);mUniquekey = "c322b12d56a3125e73e7b2978ff846c0";mContent = getArguments().getString(CONTENT);}}public void onActivityCreated(@Nullable Bundle savedInstanceState) {Window window = getDialog().getWindow();super.onActivityCreated(savedInstanceState);getDialog().setCancelable(true);getDialog().setCanceledOnTouchOutside(true);//設(shè)置背景為透明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() {public void onDismiss(DialogInterface dialog) {dismissDialog();}});}public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentmBinding = DataBindingUtil.bind(inflater.inflate(R.layout.fragment_commend_input_dialog, container, false));return mBinding.getRoot();}public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);mBinding.mTvSend.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {String content = mBinding.mEditText.getText().toString();if (TextUtils.isEmpty(content)) {Toast.makeText(App.getInstance(), "內(nèi)容不能為空", 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() {public void run() {InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);Rect r = new Rect();//獲取當(dāng)前界面可視部分getActivity().getWindow().getDecorView().getRootView().getWindowVisibleDisplayFrame(r);//獲取屏幕的高度int screenHeight = getActivity().getWindow().getDecorView().getRootView().getHeight();//此處就是用來獲取鍵盤的高度的, 在鍵盤沒有彈出的時候 此高度為0 鍵盤彈出的時候為一個正數(shù)mAllowableErrorHeight = screenHeight - r.bottom;setOnKeyBordListener();}},100);mBinding.mEditText.addTextChangedListener(new TextWatcher() {public void beforeTextChanged(CharSequence s, int start, int count, int after) {}public void onTextChanged(CharSequence s, int start, int before, int count) {String content = mBinding.mEditText.getText().toString();if(mOnDisMissCallBack != null){mOnDisMissCallBack.onCommendTextChanged(content);}}public void afterTextChanged(Editable s) {}});//設(shè)置已經(jīng)填過的文字,以及移動光標(biāo)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() {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();//獲取當(dāng)前界面可視部分getActivity().getWindow().getDecorView().getRootView().getWindowVisibleDisplayFrame(r);//獲取屏幕的高度int screenHeight = getActivity().getWindow().getDecorView().getRootView().getHeight();//此處就是用來獲取鍵盤的高度的, 在鍵盤沒有彈出的時候 此高度為0 鍵盤彈出的時候為一個正數(shù)int heightDifference = screenHeight - r.bottom;if (heightDifference > mAllowableErrorHeight && mLastDiff >= 0) {//開軟鍵盤} else {//關(guān)閉軟鍵盤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();// //獲取當(dāng)前界面可視部分// getActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(r);// //獲取屏幕的高度// int screenHeight = getActivity().getWindow().getDecorView().getRootView().getHeight();//// //此處就是用來獲取鍵盤的高度的, 在鍵盤沒有彈出的時候 此高度為0 鍵盤彈出的時候為一個正數(shù)// int heightDifference = screenHeight - r.bottom;// if (heightDifference <= mAllowableErrorHeight && mLastDiff >= 0) {// //開軟鍵盤// } else {// //關(guān)閉軟鍵盤// dismissDialog();// }// Log.d("yanjin","heightDifference = "+heightDifference+" mLastDiff="+mLastDiff);// mLastDiff = heightDifference;// }// });}private void dismissDialog() {if (mOnDisMissCallBack != null) {mOnDisMissCallBack.onDismiss();}dismissAllowingStateLoss();}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;}}
布局
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><data></data><FrameLayoutandroid:id="@+id/mRoot"android:layout_width="match_parent"android:layout_height="wrap_content"tools:context=".adapter.CommendInputDialogFragment"><RelativeLayoutandroid:id="@+id/rldlgview"android:layout_width="match_parent"android:layout_height="50dp"android:background="@drawable/storke_line_1dp_dcdcdc"><TextViewandroid:id="@+id/mTvSend"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="發(fā)送"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" /><EditTextandroid: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="發(fā)個評論,說不定能火"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>
仔細(xì)觀察抖音,他是點擊彈窗外部,隱藏dialog和軟鍵盤、點擊物理返回鍵隱藏dialog、點擊軟鍵盤的收起按鈕隱藏dialog。這三點很重要
//解決點擊彈窗外部,隱藏dialog和軟鍵盤getDialog().setCanceledOnTouchOutside(true);
而點擊物理返回鍵隱藏dialog、點擊軟鍵盤的收起按鈕隱藏dialog這兩個其實都只要監(jiān)聽軟鍵盤隱藏就可以了,那怎么做呢?看下面的代碼。
private void setOnKeyBordListener() {mBinding.rldlgview.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {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();//獲取當(dāng)前界面可視部分getActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(r);//獲取屏幕的高度int screenHeight = getActivity().getWindow().getDecorView().getRootView().getHeight();//此處就是用來獲取鍵盤的高度的, 在鍵盤沒有彈出的時候 此高度為0 鍵盤彈出的時候為一個正數(shù)int heightDifference = screenHeight - r.bottom;if (heightDifference <= 0 && mLastDiff >= 0) {//開軟鍵盤} else {//關(guān)閉軟鍵盤dismissDialog();}mLastDiff = heightDifference;}});}
好,寫完收工。
哦,對了怎么調(diào)起彈窗呢?看如下代碼即可:
if(mCommendMsgDialogFragment == null){mCommendMsgDialogFragment = CommendMsgDialogFragment.newInstance(uniquekey);mCommendMsgDialogFragment.setOnDisMissCallBack(new CommendMsgDialogFragment.OnDisMissCallBack() {public void onDismiss() {mCommendMsgDialogFragment = null;}});mCommendMsgDialogFragment.show(getSupportFragmentManager(), mCommendMsgDialogFragment.getTag());}
到這里就結(jié)束啦。
評論
圖片
表情
