真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

怎么在Android項(xiàng)目中實(shí)現(xiàn)一個(gè)評(píng)論與回復(fù)功能

怎么在Android項(xiàng)目中實(shí)現(xiàn)一個(gè)評(píng)論與回復(fù)功能?針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。

創(chuàng)新互聯(lián)公司長(zhǎng)期為上1000家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為墨竹工卡企業(yè)提供專業(yè)的成都網(wǎng)站制作、成都做網(wǎng)站,墨竹工卡網(wǎng)站改版等技術(shù)服務(wù)。擁有十年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。

布局中定義

首先,我們需要在xml的布局文件中聲明ExpandableListView:

這里需要說(shuō)明兩個(gè)問(wèn)題:

  • ExpandableListView默認(rèn)為它的item加上了點(diǎn)擊效果,由于item里面還包含了childItem,所以,點(diǎn)擊后,整個(gè)item里面的內(nèi)容都會(huì)有點(diǎn)擊效果。我們可以取消其點(diǎn)擊特效,避免其影響用戶體驗(yàn),只需要設(shè)置如上代碼中的listSelector即可。

  • ExpandableListView具有默認(rèn)的分割線,可以通過(guò)divider屬性將其隱藏。

設(shè)置Adapter

正如使用listView那樣,我們需要為ExpandableListView設(shè)置一個(gè)適配器Adapter,為其綁定數(shù)據(jù)和視圖。ExpandableListView的adapter需要繼承自ExpandableListAdapter,具體代碼如下:

/**
 * Author: Moos
 * E-mail: moosphon@gmail.com
 * Date: 18/4/20.
 * Desc: 評(píng)論與回復(fù)列表的適配器
 */

public class CommentExpandAdapter extends BaseExpandableListAdapter {
 private static final String TAG = "CommentExpandAdapter";
 private List commentBeanList;
 private Context context;

 public CommentExpandAdapter(Context context, List commentBeanList) {
 this.context = context;
 this.commentBeanList = commentBeanList;
 }

 @Override
 public int getGroupCount() {
 return commentBeanList.size();
 }

 @Override
 public int getChildrenCount(int i) {
 if(commentBeanList.get(i).getReplyList() == null){
 return 0;
 }else {
 return commentBeanList.get(i).getReplyList().size()>0 ? commentBeanList.get(i).getReplyList().size():0;
 }

 }

 @Override
 public Object getGroup(int i) {
 return commentBeanList.get(i);
 }

 @Override
 public Object getChild(int i, int i1) {
 return commentBeanList.get(i).getReplyList().get(i1);
 }

 @Override
 public long getGroupId(int groupPosition) {
 return groupPosition;
 }

 @Override
 public long getChildId(int groupPosition, int childPosition) {
 return getCombinedChildId(groupPosition, childPosition);
 }

 @Override
 public boolean hasStableIds() {
 return true;
 }
 boolean isLike = false;

 @Override
 public View getGroupView(final int groupPosition, boolean isExpand, View convertView, ViewGroup viewGroup) {
 final GroupHolder groupHolder;

 if(convertView == null){
 convertView = LayoutInflater.from(context).inflate(R.layout.comment_item_layout, viewGroup, false);
 groupHolder = new GroupHolder(convertView);
 convertView.setTag(groupHolder);
 }else {
 groupHolder = (GroupHolder) convertView.getTag();
 }
 Glide.with(context).load(R.drawable.user_other)
 .diskCacheStrategy(DiskCacheStrategy.RESULT)
 .error(R.mipmap.ic_launcher)
 .centerCrop()
 .into(groupHolder.logo);
 groupHolder.tv_name.setText(commentBeanList.get(groupPosition).getNickName());
 groupHolder.tv_time.setText(commentBeanList.get(groupPosition).getCreateDate());
 groupHolder.tv_content.setText(commentBeanList.get(groupPosition).getContent());
 groupHolder.iv_like.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View view) {
 if(isLike){
  isLike = false;
  groupHolder.iv_like.setColorFilter(Color.parseColor("#aaaaaa"));
 }else {
  isLike = true;
  groupHolder.iv_like.setColorFilter(Color.parseColor("#FF5C5C"));
 }
 }
 });

 return convertView;
 }

 @Override
 public View getChildView(final int groupPosition, int childPosition, boolean b, View convertView, ViewGroup viewGroup) {
 final ChildHolder childHolder;
 if(convertView == null){
 convertView = LayoutInflater.from(context).inflate(R.layout.comment_reply_item_layout,viewGroup, false);
 childHolder = new ChildHolder(convertView);
 convertView.setTag(childHolder);
 }
 else {
 childHolder = (ChildHolder) convertView.getTag();
 }

 String replyUser = commentBeanList.get(groupPosition).getReplyList().get(childPosition).getNickName();
 if(!TextUtils.isEmpty(replyUser)){
 childHolder.tv_name.setText(replyUser + ":");
 }

 childHolder.tv_content.setText(commentBeanList.get(groupPosition).getReplyList().get(childPosition).getContent());

 return convertView;
 }

 @Override
 public boolean isChildSelectable(int i, int i1) {
 return true;
 }

 private class GroupHolder{
 private CircleImageView logo;
 private TextView tv_name, tv_content, tv_time;
 private ImageView iv_like;
 public GroupHolder(View view) {
 logo = view.findViewById(R.id.comment_item_logo);
 tv_content = view.findViewById(R.id.comment_item_content);
 tv_name = view.findViewById(R.id.comment_item_userName);
 tv_time = view.findViewById(R.id.comment_item_time);
 iv_like = view.findViewById(R.id.comment_item_like);
 }
 }

 private class ChildHolder{
 private TextView tv_name, tv_content;
 public ChildHolder(View view) {
 tv_name = (TextView) view.findViewById(R.id.reply_item_user);
 tv_content = (TextView) view.findViewById(R.id.reply_item_content);
 }
 }
}

一般情況下,我們自定義自己的ExpandableListAdapter后,需要實(shí)現(xiàn)以下幾個(gè)方法:

  • 構(gòu)造方法,這個(gè)應(yīng)該無(wú)需多說(shuō)了,一般用來(lái)初始化數(shù)據(jù)等操作。

  • getGroupCount,返回group分組的數(shù)量,在當(dāng)前需求中指代評(píng)論的數(shù)量。

  • getChildrenCount,返回所在group中child的數(shù)量,這里指代當(dāng)前評(píng)論對(duì)應(yīng)的回復(fù)數(shù)目。

  • getGroup,返回group的實(shí)際數(shù)據(jù),這里指的是當(dāng)前評(píng)論數(shù)據(jù)。

  • getChild,返回group中某個(gè)child的實(shí)際數(shù)據(jù),這里指的是當(dāng)前評(píng)論的某個(gè)回復(fù)數(shù)據(jù)。

  • getGroupId,返回分組的id,一般將當(dāng)前group的位置傳給它。

  • getChildId,返回分組中某個(gè)child的id,一般也將child當(dāng)前位置傳給它,不過(guò)為了避免重復(fù),可以使用getCombinedChildId(groupPosition, childPosition);來(lái)獲取id并返回。

  • hasStableIds,表示分組和子選項(xiàng)是否持有穩(wěn)定的id,這里返回true即可。

  • isChildSelectable,表示分組中的child是否可以選中,這里返回true。

  • getGroupView,即返回group的視圖,一般在這里進(jìn)行一些數(shù)據(jù)和視圖綁定的工作,一般為了復(fù)用和高效,可以自定義ViewHolder,用法與listview一樣,這里就不多說(shuō)了。

  • getChildView,返回分組中child子項(xiàng)的視圖,比較容易理解,第一個(gè)參數(shù)是當(dāng)前group所在的位置,第二個(gè)參數(shù)是當(dāng)前child所在位置。

這里的數(shù)據(jù)是我自己做的模擬數(shù)據(jù),不過(guò)應(yīng)該算是較為通用的格式了,大體格式如下:

怎么在Android項(xiàng)目中實(shí)現(xiàn)一個(gè)評(píng)論與回復(fù)功能

一般情況下,我們后臺(tái)會(huì)通過(guò)接口返回給我們一部分?jǐn)?shù)據(jù),如果想要查看更多評(píng)論,需要跳轉(zhuǎn)到“更多頁(yè)面”去查看,這里為了方便,我們只考慮加載部分?jǐn)?shù)據(jù)。

Activity中使用

接下來(lái),我們就需要在activity中顯示評(píng)論和回復(fù)的二級(jí)列表了:

private ExpandableListView expandableListView;
private CommentExpandAdapter adapter;
private CommentBean commentBean;
private List commentsList;

...

@Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 initView();
 }

 private void initView() {

 expandableListView = findViewById(R.id.detail_page_lv_comment);
 initExpandableListView(commentsList);
 }

 /**
 * 初始化評(píng)論和回復(fù)列表
 */
 private void initExpandableListView(final List commentList){
 expandableListView.setGroupIndicator(null);
 //默認(rèn)展開(kāi)所有回復(fù)
 adapter = new CommentExpandAdapter(this, commentList);
 expandableListView.setAdapter(adapter);
 for(int i = 0; i>>"+commentList.get(groupPosition).getId());

//  if(isExpanded){
//   expandableListView.collapseGroup(groupPosition);
//  }else {
//   expandableListView.expandGroup(groupPosition, true);
//  }

  return true;
  }
 });

 expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
  @Override
  public boolean onChildClick(ExpandableListView expandableListView, View view, int groupPosition, int childPosition, long l) {
  Toast.makeText(MainActivity.this,"點(diǎn)擊了回復(fù)",Toast.LENGTH_SHORT).show();
  return false;
  }
 });

 expandableListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
  @Override
  public void onGroupExpand(int groupPosition) {
  //toast("展開(kāi)第"+groupPosition+"個(gè)分組");

  }
 });

 }

 /**
 * by moos on 2018/04/20
 * func:生成測(cè)試數(shù)據(jù)
 * @return 評(píng)論數(shù)據(jù)
 */
 private List generateTestData(){
 Gson gson = new Gson();
 commentBean = gson.fromJson(testJson, CommentBean.class);
 List commentList = commentBean.getData().getList();
 return commentList;
 }

就以上代碼作一下簡(jiǎn)單說(shuō)明:

1、ExpandableListView在默認(rèn)情況下會(huì)為我們自帶分組的icon(??),當(dāng)前需求下,我們根本不需要展示,可以通過(guò)expandableListView.setGroupIndicator(null)來(lái)隱藏。

2、一般情況下,我們可能需要默認(rèn)展開(kāi)所有的分組,我就可以通過(guò)循環(huán)來(lái)調(diào)用expandableListView.expandGroup(i);方法。

3、ExpandableListView為我們提供了group和child的點(diǎn)擊事件,分別通過(guò)setOnGroupClickListener和setOnChildClickListener來(lái)設(shè)置。值得注意的是,group的點(diǎn)擊事件里如果我們返回的是false,那么我們點(diǎn)擊group就會(huì)自動(dòng)展開(kāi),但我這里碰到一個(gè)問(wèn)題,當(dāng)我返回false時(shí),第一條評(píng)論數(shù)據(jù)會(huì)多出一條。通過(guò)百度查找方法,雖然很多類似問(wèn)題,但終究沒(méi)有解決,最后我返回了ture,并通過(guò)以下代碼手動(dòng)展開(kāi)和收縮就可以了:

if(isExpanded){
 expandableListView.collapseGroup(groupPosition);
}else {
 expandableListView.expandGroup(groupPosition, true);
}

4、此外,我們還可以通過(guò)setOnGroupExpandListener和setOnGroupCollapseListener來(lái)監(jiān)聽(tīng)ExpandableListView的分組展開(kāi)和收縮的狀態(tài)。

評(píng)論和回復(fù)功能

為了模擬整個(gè)評(píng)論和回復(fù)功能,我們還需要手動(dòng)插入收據(jù)并刷新數(shù)據(jù)列表。這里我就簡(jiǎn)單做一下模擬,請(qǐng)忽略一些UI上的細(xì)節(jié)。

插入評(píng)論數(shù)據(jù)

插入評(píng)論數(shù)據(jù)比較簡(jiǎn)單,只需要在list中插入一條數(shù)據(jù)并刷新即可:

String commentContent = commentText.getText().toString().trim();
if(!TextUtils.isEmpty(commentContent)){

 //commentOnWork(commentContent);
 dialog.dismiss();
 CommentDetailBean detailBean = new CommentDetailBean("小明", commentContent,"剛剛");
 adapter.addTheCommentData(detailBean);
 Toast.makeText(MainActivity.this,"評(píng)論成功",Toast.LENGTH_SHORT).show();

}else {
 Toast.makeText(MainActivity.this,"評(píng)論內(nèi)容不能為空",Toast.LENGTH_SHORT).show();
}

adapter中的addTheCommentData方法如下:

/**
 * by moos on 2018/04/20
 * func:評(píng)論成功后插入一條數(shù)據(jù)
 * @param commentDetailBean 新的評(píng)論數(shù)據(jù)
 */
public void addTheCommentData(CommentDetailBean commentDetailBean){
 if(commentDetailBean!=null){

 commentBeanList.add(commentDetailBean);
 notifyDataSetChanged();
 }else {
 throw new IllegalArgumentException("評(píng)論數(shù)據(jù)為空!");
 }
}

代碼比較容易理解,就不多做說(shuō)明了。

插入回復(fù)數(shù)據(jù)

首先,我們需要實(shí)現(xiàn)點(diǎn)擊某一條評(píng)論,然后@ta,那么我們需要在group的點(diǎn)擊事件里彈起回復(fù)框:

expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
  @Override
  public boolean onGroupClick(ExpandableListView expandableListView, View view, int groupPosition, long l) {

  showReplyDialog(groupPosition);
  return true;
  }
 });

......

/**
 * by moos on 2018/04/20
 * func:彈出回復(fù)框
 */
 private void showReplyDialog(final int position){
 dialog = new BottomSheetDialog(this);
 View commentView = LayoutInflater.from(this).inflate(R.layout.comment_dialog_layout,null);
 final EditText commentText = (EditText) commentView.findViewById(R.id.dialog_comment_et);
 final Button bt_comment = (Button) commentView.findViewById(R.id.dialog_comment_bt);
 commentText.setHint("回復(fù) " + commentsList.get(position).getNickName() + " 的評(píng)論:");
 dialog.setContentView(commentView);
 bt_comment.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
  String replyContent = commentText.getText().toString().trim();
  if(!TextUtils.isEmpty(replyContent)){

   dialog.dismiss();
   ReplyDetailBean detailBean = new ReplyDetailBean("小紅",replyContent);
   adapter.addTheReplyData(detailBean, position);
   Toast.makeText(MainActivity.this,"回復(fù)成功",Toast.LENGTH_SHORT).show();
  }else {
   Toast.makeText(MainActivity.this,"回復(fù)內(nèi)容不能為空",Toast.LENGTH_SHORT).show();
  }
  }
 });
 commentText.addTextChangedListener(new TextWatcher() {
  @Override
  public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

  }

  @Override
  public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
  if(!TextUtils.isEmpty(charSequence) && charSequence.length()>2){
   bt_comment.setBackgroundColor(Color.parseColor("#FFB568"));
  }else {
   bt_comment.setBackgroundColor(Color.parseColor("#D8D8D8"));
  }
  }

  @Override
  public void afterTextChanged(Editable editable) {

  }
 });
 dialog.show();
 }

插入回復(fù)的數(shù)據(jù)與上面插入評(píng)論類似,這里貼一下adapter中的代碼:

/**
 * by moos on 2018/04/20
 * func:回復(fù)成功后插入一條數(shù)據(jù)
 * @param replyDetailBean 新的回復(fù)數(shù)據(jù)
 */
public void addTheReplyData(ReplyDetailBean replyDetailBean, int groupPosition){
 if(replyDetailBean!=null){
 Log.e(TAG, "addTheReplyData: >>>>該刷新回復(fù)列表了:"+replyDetailBean.toString() );
 if(commentBeanList.get(groupPosition).getReplyList() != null ){
  commentBeanList.get(groupPosition).getReplyList().add(replyDetailBean);
 }else {
  List replyList = new ArrayList<>();
  replyList.add(replyDetailBean);
  commentBeanList.get(groupPosition).setReplyList(replyList);
 }
 notifyDataSetChanged();
 }else {
 throw new IllegalArgumentException("回復(fù)數(shù)據(jù)為空!");
 }
}

需要注意一點(diǎn),由于不一定所有的評(píng)論都有回復(fù)數(shù)據(jù),所以在插入數(shù)據(jù)前我們要判斷ReplyList是否為空,如果不為空,直接獲取當(dāng)前評(píng)論的回復(fù)列表,并插入數(shù)據(jù);如果為空,需要new一個(gè)ReplyList,插入數(shù)據(jù)后還要為評(píng)論set一下ReplyList。

解決CoordinatorLayout與ExpandableListView嵌套問(wèn)題

如果你不需要使用CoordinatorLayout或者NestedScrollView,可以跳過(guò)本小節(jié)。一般情況下,我們產(chǎn)品為了更好的用戶體驗(yàn),還需要我們加上類似的頂部視差效果或者下拉刷新等,這就要我們處理一些常見(jiàn)的嵌套滑動(dòng)問(wèn)題了。

由于CoordinatorLayout實(shí)現(xiàn)NestedScrollingParent接口,RecycleView實(shí)現(xiàn)了NestedScrollingChild接口,所以就可以在NestedScrollingChildHelper的幫助下實(shí)現(xiàn)嵌套滑動(dòng),那么我們也可以通過(guò)自定義的ExpandableListView實(shí)現(xiàn)NestedScrollingChild接口來(lái)達(dá)到同樣的效果:

/**
 * Author: Moos
 * E-mail: moosphon@gmail.com
 * Date: 18/4/20.
 * Desc: 自定義ExpandableListView,解決與CoordinatorLayout滑動(dòng)沖突問(wèn)題
 */

public class CommentExpandableListView extends ExpandableListView implements NestedScrollingChild{
 private NestedScrollingChildHelper mScrollingChildHelper;


 public CommentExpandableListView(Context context, AttributeSet attrs) {
 super(context, attrs);
 mScrollingChildHelper = new NestedScrollingChildHelper(this);
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  setNestedScrollingEnabled(true);
 }

 }


 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
 super.onMeasure(widthMeasureSpec, expandSpec);

 }

 @Override
 public void setNestedScrollingEnabled(boolean enabled) {
 mScrollingChildHelper.setNestedScrollingEnabled(enabled);
 }

 @Override
 public boolean isNestedScrollingEnabled() {
 return mScrollingChildHelper.isNestedScrollingEnabled();
 }

 @Override
 public boolean startNestedScroll(int axes) {
 return mScrollingChildHelper.startNestedScroll(axes);
 }

 @Override
 public void stopNestedScroll() {
 mScrollingChildHelper.stopNestedScroll();
 }

 @Override
 public boolean hasNestedScrollingParent() {
 return mScrollingChildHelper.hasNestedScrollingParent();
 }

 @Override
 public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
     int dyUnconsumed, int[] offsetInWindow) {
 return mScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
  dxUnconsumed, dyUnconsumed, offsetInWindow);
 }

 @Override
 public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
 return mScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
 }

 @Override
 public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
 return mScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
 }

 @Override
 public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
 return mScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
 }
}

關(guān)于怎么在Android項(xiàng)目中實(shí)現(xiàn)一個(gè)評(píng)論與回復(fù)功能問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。


分享文章:怎么在Android項(xiàng)目中實(shí)現(xiàn)一個(gè)評(píng)論與回復(fù)功能
網(wǎng)頁(yè)地址:http://weahome.cn/article/psdjgd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部