企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## Android应用开发-小巫CSDN博客客户端之获取评论列表 上一篇博客介绍了博文详细内容的业务逻辑实现,本篇博客介绍小巫CSDN博客客户端的最后一项功能,获取评论列表,这个功能的实现跟前面获取文章列表和文章详细的内容不一样,CSDN博客获取评论是通过js来请求服务器加载评论列表的,返回数据为json数据,我们这里要做的事情就是找到这样的一个js文件,再找到请求url的拼接字符串,然后根据我们的需求,请求文章的评论列表获取到当前文章的评论json数据,然后进行解析工作,最后展示到我们的界面当中。 如果没有仔细分析html代码的童鞋,可能发现不了这一点,小巫在开发这个客户端的时候,一时也获取不到评论列表,后来通过与CSDN技术的交流之后,我再仔细查看才找到了关于博客的请求方式,这里使用jsoup无法模拟javascript的加载,所以只能通过自己查看js代码,找到请求的url,下面笔者会告诉大家怎么来做这件事。 小巫这里找一篇有评论的博文,比如以下这篇: [http://blog.csdn.net/wwj_748/article/details/39726051](http://blog.csdn.net/wwj_748/article/details/39726051) ![](https://box.kancloud.cn/2016-02-24_56cd63d2a8a34.jpg) 我们可以看到这篇文章的底部是我们的文章评论列表,有别人评论的也有自己回复的。用同样的方式,F12查看源代码,或者查看元素定位到评论内容,如下所示: ![](https://box.kancloud.cn/2016-02-24_56cd63d2f1f2d.jpg) 这时我们点击进去查看相应的js文件,去看看能不能找到我们想要的东西: 哎呀,很不小心就被我发现了我想要的东西: ![](https://box.kancloud.cn/2016-02-24_56cd63d8c7122.jpg) 从上面我们可以分析出,获取文章的评论列表需要请求类似以下的地址: "http://blog.csdn.net/wwj_748/comment/list/39726051?page=1,刚开始小巫并不知道这样的请求地址,是通过以上的方式才得知的。我们请求一篇文章需要知道对应文章的filename和pageIndex,然后以下面这种形式拼接: ~~~ /** * 返回博文评论列表链接 * * @param filename * 文件名 * @param pageIndex * 页数 * @return */ public static String getCommentListURL(String filename, String pageIndex) { return "http://blog.csdn.net/wwj_748/comment/list/" + filename + "?page=" + pageIndex; } ~~~ 到了这一步基本上解决了最麻烦的事情,下面是业务逻辑的实现: /BlogClient/src/com/xiaowu/blogclient/BlogCommentActivity.java ~~~ package com.xiaowu.blogclient; import java.util.List; import me.maxwin.view.IXListViewLoadMore; import me.maxwin.view.IXListViewRefreshListener; import me.maxwin.view.XListView; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.xiaowu.blogclient.adapter.CommentAdapter; import com.xiaowu.blogclient.model.Comment; import com.xiaowu.blogclient.model.Page; import com.xiaowu.blogclient.util.Constants; import com.xiaowu.blogclient.util.DateUtil; import com.xiaowu.blogclient.util.HttpUtil; import com.xiaowu.blogclient.util.JsoupUtil; import com.xiaowu.blogclient.util.URLUtil; /** * 2014/8/13 * * 博客评论列表 * * @author wwj_748 * */ public class BlogCommentActivity extends Activity implements IXListViewRefreshListener, IXListViewLoadMore { private XListView listView; private CommentAdapter adapter; private ProgressBar progressBar; private ImageView reLoadImageView; private ImageView backBtn; private TextView commentTV; public static String commentCount = ""; private Page page; private String filename; private int pageIndex = 1; private int pageSize = 20; @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); setContentView(R.layout.activity_comment); init(); initComponent(); listView.setRefreshTime(DateUtil.getDate()); // 设置刷新时间 listView.startRefresh(); // 开始刷新 } // 初始化 private void init() { filename = getIntent().getExtras().getString("filename"); // 获得文件名 page = new Page(); adapter = new CommentAdapter(this); } // 初始化组件 private void initComponent() { progressBar = (ProgressBar) findViewById(R.id.newsContentPro); reLoadImageView = (ImageView) findViewById(R.id.reLoadImage); reLoadImageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { System.out.println("click"); reLoadImageView.setVisibility(View.INVISIBLE); progressBar.setVisibility(View.VISIBLE); new MainTask().execute(Constants.DEF_TASK_TYPE.REFRESH); } }); backBtn = (ImageView) findViewById(R.id.backBtn); backBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { finish(); } }); commentTV = (TextView) findViewById(R.id.comment); listView = (XListView) findViewById(R.id.listview); listView.setAdapter(adapter); listView.setPullRefreshEnable(this); listView.setPullLoadEnable(this); listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { } }); } @Override public void finish() { super.finish(); // 退出动画 overridePendingTransition(R.anim.push_no, R.anim.push_right_out); } private class MainTask extends AsyncTask<String, Void, Integer> { @Override protected Integer doInBackground(String... params) { // 获得返回json字符串 String temp = HttpUtil.httpGet(URLUtil.getCommentListURL(filename, page.getCurrentPage())); if (temp == null) { return Constants.DEF_RESULT_CODE.ERROR; } // 获得评论列表 List<Comment> list = JsoupUtil.getBlogCommentList(temp, Integer.valueOf(page.getCurrentPage()), pageSize); if (list.size() == 0) { return Constants.DEF_RESULT_CODE.NO_DATA; } if (params[0].equals(Constants.DEF_TASK_TYPE.LOAD)) { adapter.addList(list); return Constants.DEF_RESULT_CODE.LOAD; } else { adapter.setList(list); return Constants.DEF_RESULT_CODE.REFRESH; } } @Override protected void onPostExecute(Integer result) { if (result == Constants.DEF_RESULT_CODE.ERROR) { Toast.makeText(getApplicationContext(), "网络信号不佳", Toast.LENGTH_SHORT).show(); listView.stopRefresh(DateUtil.getDate()); listView.stopLoadMore(); reLoadImageView.setVisibility(View.VISIBLE); } else if (result == Constants.DEF_RESULT_CODE.NO_DATA) { Toast.makeText(getApplicationContext(), "无更多评论", Toast.LENGTH_SHORT).show(); listView.stopLoadMore(); listView.stopRefresh(DateUtil.getDate()); commentTV.setText("共有评论:" + commentCount); } else if (result == Constants.DEF_RESULT_CODE.LOAD) { page.addPage(); pageIndex++; adapter.notifyDataSetChanged(); listView.stopLoadMore(); } else if (result == Constants.DEF_RESULT_CODE.REFRESH) { adapter.notifyDataSetChanged(); listView.stopRefresh(DateUtil.getDate()); page.setPage(2); commentTV.setText("共有评论:" + commentCount); // 显示评论数 } progressBar.setVisibility(View.INVISIBLE); super.onPostExecute(result); } } // 加载更多 @Override public void onLoadMore() { new MainTask().execute(Constants.DEF_TASK_TYPE.LOAD); } // 刷新评论 @Override public void onRefresh() { page.setPage(1); new MainTask().execute(Constants.DEF_TASK_TYPE.REFRESH); } } ~~~ /BlogClient/src/com/xiaowu/blogclient/adapter/CommentAdapter.java ~~~ package com.xiaowu.blogclient.adapter; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.graphics.Bitmap; import android.text.Html; import android.text.SpannableStringBuilder; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer; import com.xiaowu.blogclient.R; import com.xiaowu.blogclient.model.Comment; import com.xiaowu.blogclient.util.Constants; /** * 评论列表适配器 * * @author wwj_748 * */ public class CommentAdapter extends BaseAdapter { private ViewHolder holder; private LayoutInflater layoutInflater; private Context context; private List<Comment> list; private SpannableStringBuilder htmlSpannable; private ImageLoader imageLoader = ImageLoader.getInstance(); private DisplayImageOptions options; private String replyText; public CommentAdapter(Context c) { super(); layoutInflater = (LayoutInflater) LayoutInflater.from(c); list = new ArrayList<Comment>(); imageLoader.init(ImageLoaderConfiguration.createDefault(c)); options = new DisplayImageOptions.Builder() .showStubImage(R.drawable.csdn) .showImageForEmptyUri(R.drawable.csdn) .showImageOnFail(R.drawable.csdn) .cacheInMemory().cacheOnDisc() .imageScaleType(ImageScaleType.EXACTLY) .bitmapConfig(Bitmap.Config.RGB_565) .displayer(new FadeInBitmapDisplayer(300)).build(); } public void setList(List<Comment> list) { this.list = list; } public void addList(List<Comment> list) { this.list.addAll(list); } public void clearList() { this.list.clear(); } public List<Comment> getList() { return list; } public void removeItem(int position) { if (list.size() > 0) { list.remove(position); } } @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { Comment item = list.get(position); // 获取评论项 if (null == convertView) { holder = new ViewHolder(); switch (item.getType()) { case Constants.DEF_COMMENT_TYPE.PARENT: // 父项 convertView = layoutInflater.inflate(R.layout.comment_item, null); holder.name = (TextView) convertView.findViewById(R.id.name); holder.content = (TextView) convertView .findViewById(R.id.content); holder.date = (TextView) convertView.findViewById(R.id.date); holder.reply = (TextView) convertView .findViewById(R.id.replyCount); holder.userface = (ImageView) convertView.findViewById(R.id.userface); break; case Constants.DEF_COMMENT_TYPE.CHILD: // 子项 convertView = layoutInflater.inflate( R.layout.comment_child_item, null); holder.name = (TextView) convertView.findViewById(R.id.name); holder.content = (TextView) convertView .findViewById(R.id.content); holder.date = (TextView) convertView.findViewById(R.id.date); break; } convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } if (null != item) { switch (item.getType()) { case Constants.DEF_COMMENT_TYPE.PARENT: // 主题项 holder.name.setText(item.getUsername()); holder.content.setText(Html.fromHtml(item.getContent())); // 显示评论内容 holder.date.setText(item.getPostTime()); // holder.reply.setText(item.getReplyCount()); imageLoader.displayImage(item.getUserface(), holder.userface, options);// 显示头像 break; case Constants.DEF_COMMENT_TYPE.CHILD: // 回复项 holder.name.setText(item.getUsername()); replyText = item.getContent().replace("[reply]", "【"); replyText = replyText.replace("[/reply]", "】"); holder.content.setText(Html.fromHtml(replyText)); holder.date.setText(item.getPostTime()); break; default: break; } } return convertView; } @Override public int getViewTypeCount() { return 2; } @Override public int getItemViewType(int position) { switch (list.get(position).getType()) { case Constants.DEF_COMMENT_TYPE.PARENT: // 父节点 return 0; case Constants.DEF_COMMENT_TYPE.CHILD: // 子节点 return 1; } return 1; } @Override public boolean isEnabled(int position) { return true; } private class ViewHolder { TextView id; TextView date; TextView name; TextView content; ImageView userface; TextView reply; } } ~~~ 最终效果图如下: ![](https://box.kancloud.cn/2016-02-24_56cd63d93b627.jpg) 最后: 关于小巫CSDN博客客户端的开发基本上都介绍完了,更多详细的实现请到以下链接下载源码查看: [http://download.csdn.net/detail/wwj_748/7912513](http://download.csdn.net/detail/wwj_748/7912513)