实现app上对csdn的文章查看,以及文章中图片的保存 (制作csdn app 完结篇)
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24022165
今天给大家带来CSDN的完结篇,即加入文章的查看和文章中图片的保存~
今天的目标:
首先是对控件使用的考虑,既然是网络上的文章,可能首先想到的就是webview,这里直接把页面载入到webview中是肯定不行的,首先得把页面上的数据解析,然后可能需要一个html的模版,然后把数据填充到模版,再将模版用于webview的展示。想了想,还是不是很方面,因为不确定文章中的段落、图片的数量和位置。所以最终照着网络上流传的版本使用List实现。
思路:把页面上的数据解析成 标题、摘要、段落(*)、图片(*),自定以一个对象,解析完成后生成一个List,当然顺序一定要和原文的一直。然后针对标题、摘要、段落、图片各做一个List的item的布局,最终显示。
好了,先简单看下csdn文章页的html:
我们在原先的代表上,添加对这样html页面的解析:
首先是封装的对象:
package com.zhy.bean; import java.util.List; public class NewsDto { private List<News> newses; private String nextPageUrl ; public List<News> getNewses() { return newses; } public void setNewses(List<News> newses) { this.newses = newses; } public String getNextPageUrl() { return nextPageUrl; } public void setNextPageUrl(String nextPageUrl) { this.nextPageUrl = nextPageUrl; } }
package com.zhy.bean; public class News { public static interface NewsType { public static final int TITLE = 1; public static final int SUMMARY = 2; public static final int CONTENT = 3; public static final int IMG = 4; public static final int BOLD_TITLE = 5; } /** * 标题 */ private String title; /** * 摘要 */ private String summary; /** * 内容 */ private String content; /** * 图片链接 */ private String imageLink; /** * 类型 */ private int type; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getSummary() { return summary; } public void setSummary(String summary) { this.summary = summary; this.type = NewsType.SUMMARY; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getImageLink() { return imageLink; } public void setImageLink(String imageLink) { this.imageLink = imageLink; this.type = NewsType.IMG; } public int getType() { return type; } public void setType(int type) { this.type = type; } @Override public String toString() { return "News [title=" + title + ", summary=" + summary + ", content=" + content + ", imageLink=" + imageLink + ", type=" + type + "]"; } }添加了一个新的业务方法,把html字符串转化为List对象:
/** * 根据文章的url返回一个NewsDto对象 * * @return * @throws CommonException */ public NewsDto getNews(String urlStr) throws CommonException { NewsDto newsDto = new NewsDto(); List<News> newses = new ArrayList<News>(); String htmlStr = DataUtil.doGet(urlStr); Document doc = Jsoup.parse(htmlStr); // 获得文章中的第一个detail Element detailEle = doc.select(".left .detail").get(0); // 标题 Element titleEle = detailEle.select("h1.title").get(0); News news = new News(); news.setTitle(titleEle.text()); news.setType(NewsType.TITLE); newses.add(news); // 摘要 Element summaryEle = detailEle.select("div.summary").get(0); news = new News(); news.setSummary(summaryEle.text()); newses.add(news); // 内容 Element contentEle = detailEle.select("div.con.news_content").get(0); Elements childrenEle = contentEle.children(); for (Element child : childrenEle) { Elements imgEles = child.getElementsByTag("img"); // 图片 if (imgEles.size() > 0) { for (Element imgEle : imgEles) { if (imgEle.attr("src").equals("")) continue; news = new News(); news.setImageLink(imgEle.attr("src")); newses.add(news); } } // 移除图片 imgEles.remove(); if (child.text().equals("")) continue; news = new News(); news.setType(NewsType.CONTENT); try { if(child.children().size()==1) { Element cc = child.child(0); if(cc.tagName().equals("b")) { news.setType(NewsType.BOLD_TITLE); } } } catch (IndexOutOfBoundsException e) { e.printStackTrace(); } news.setContent(child.outerHtml()); newses.add(news); } newsDto.setNewses(newses); return newsDto; }测试代码:
@org.junit.Test public void test02() { NewsItemBiz biz = new NewsItemBiz(); try { NewsDto newsDto = biz.getNews("http://www.csdn.net/article/2014-04-17/2819363-all-about-ddos"); List<News> newses = newsDto.getNewses(); for(News news : newses) { System.out.println(news); } System.out.println("-----"); System.out.println(newsDto.getNextPageUrl());; } catch (CommonException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
然后我们可以拿到这样的结果数据:
好了,现在准备把解析完成的数据用到我们的app上。
上一教程已经完成了Xlist的显示,上拉与下拉,现在给它添加OnItemClickListener:
mXListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { NewsItem newsItem = mDatas.get(position-1); Intent intent = new Intent(getActivity(), NewsContentActivity.class); intent.putExtra("url", newsItem.getLink()); startActivity(intent); } });
到达显示内容的Activity页面:
package com.zhy.csdndemo; import java.util.List; import me.maxwin.view.IXListViewLoadMore; import me.maxwin.view.XListView; import android.app.Activity; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.os.Looper; import android.view.View; import android.widget.AdapterView; import android.widget.Toast; import android.widget.AdapterView.OnItemClickListener; import android.widget.ProgressBar; import com.zhy.bean.CommonException; import com.zhy.bean.News; import com.zhy.biz.NewsItemBiz; import com.zhy.csdndemo.adapter.NewContentAdapter; public class NewsContentActivity extends Activity implements IXListViewLoadMore { private XListView mListView; /** * 该页面的url */ private String url; private NewsItemBiz mNewsItemBiz; private List<News> mDatas; private ProgressBar mProgressBar; private NewContentAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.news_content); mNewsItemBiz = new NewsItemBiz(); Bundle extras = getIntent().getExtras(); url = extras.getString("url"); mAdapter = new NewContentAdapter(this); mListView = (XListView) findViewById(R.id.id_listview); mProgressBar = (ProgressBar) findViewById(R.id.id_newsContentPro); mListView.setAdapter(mAdapter); mListView.disablePullRefreash(); mListView.setPullLoadEnable(this); mListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { News news = mDatas.get(position - 1); String imageLink = news.getImageLink(); //Toast.makeText(NewContentActivity.this, imageLink, 1).show(); Intent intent = new Intent(NewsContentActivity.this,ImageShowActivity.class); intent.putExtra("url", imageLink); startActivity(intent); } }); mProgressBar.setVisibility(View.VISIBLE); new LoadDataTask().execute(); } @Override public void onLoadMore() { } class LoadDataTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { try { mDatas = mNewsItemBiz.getNews(url).getNewses(); } catch (CommonException e) { Looper.prepare(); Toast.makeText(getApplicationContext(), e.getMessage(), 1).show(); Looper.loop(); } return null; } @Override protected void onPostExecute(Void result) { if(mDatas == null) return ; mAdapter.addList(mDatas); mAdapter.notifyDataSetChanged(); mProgressBar.setVisibility(View.GONE); } } /** * 点击返回按钮 * @param view */ public void back(View view) { finish(); } }
接下来看这个Activity中ListView的Adapter
package com.zhy.csdndemo.adapter; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.graphics.Bitmap; import android.text.Html; import android.util.Log; 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.zhy.bean.News; import com.zhy.bean.News.NewsType; import com.zhy.csdndemo.R; public class NewContentAdapter extends BaseAdapter { private LayoutInflater mInflater; private List<News> mDatas = new ArrayList<News>(); private ImageLoader imageLoader = ImageLoader.getInstance(); private DisplayImageOptions options; public NewContentAdapter(Context context) { mInflater = LayoutInflater.from(context); imageLoader.init(ImageLoaderConfiguration.createDefault(context)); options = new DisplayImageOptions.Builder().showStubImage(R.drawable.images) .showImageForEmptyUri(R.drawable.images).showImageOnFail(R.drawable.images).cacheInMemory() .cacheOnDisc().imageScaleType(ImageScaleType.EXACTLY).bitmapConfig(Bitmap.Config.RGB_565) .displayer(new FadeInBitmapDisplayer(300)).build(); } public void addList(List<News> datas) { mDatas.addAll(datas); } @Override public int getCount() { return mDatas.size(); } @Override public Object getItem(int position) { return mDatas.get(position); } @Override public long getItemId(int position) { return position; } @Override public int getItemViewType(int position) { switch (mDatas.get(position).getType()) { case NewsType.TITLE: return 0; case NewsType.SUMMARY: return 1; case NewsType.CONTENT: return 2; case NewsType.IMG: return 3; case NewsType.BOLD_TITLE: return 4; } return -1; } @Override public int getViewTypeCount() { return 5; } @Override public boolean isEnabled(int position) { switch (mDatas.get(position).getType()) { case NewsType.IMG: return true; default: return false; } } @Override public View getView(int position, View convertView, ViewGroup parent) { News news = mDatas.get(position); // 获取当前项数据 Log.e("xxx", news.toString()); ViewHolder holder = null; if (null == convertView) { holder = new ViewHolder(); switch (news.getType()) { case NewsType.TITLE: convertView = mInflater.inflate(R.layout.news_content_title_item, null); holder.mTextView = (TextView) convertView.findViewById(R.id.text); break; case NewsType.SUMMARY: convertView = mInflater.inflate(R.layout.news_content_summary_item, null); holder.mTextView = (TextView) convertView.findViewById(R.id.text); break; case NewsType.CONTENT: convertView = mInflater.inflate(R.layout.news_content_item, null); holder.mTextView = (TextView) convertView.findViewById(R.id.text); break; case NewsType.IMG: convertView = mInflater.inflate(R.layout.news_content_img_item, null); holder.mImageView = (ImageView) convertView.findViewById(R.id.imageView); break; case NewsType.BOLD_TITLE: convertView = mInflater.inflate(R.layout.news_content_bold_title_item, null); holder.mTextView = (TextView) convertView.findViewById(R.id.text); break; } convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } if (null != news) { switch (news.getType()) { case NewsType.IMG: imageLoader.displayImage(news.getImageLink(), holder.mImageView, options); break; case NewsType.TITLE: holder.mTextView.setText(news.getTitle()); break; case NewsType.SUMMARY: holder.mTextView.setText(news.getSummary()); break; case NewsType.CONTENT: holder.mTextView.setText("\u3000\u3000"+Html.fromHtml(news.getContent())); break; case NewsType.BOLD_TITLE: holder.mTextView.setText("\u3000\u3000"+Html.fromHtml(news.getContent())); default: // holder.mTextView.setText(Html.fromHtml(item.getContent(), // null, new MyTagHandler())); // holder.content.setText(Html.fromHtml("<ul><bold>加粗</bold>sdfsdf<ul>", // null, new MyTagHandler())); break; } } return convertView; } private final class ViewHolder { TextView mTextView; ImageView mImageView; } }
我们复写了getViewTypeCount , getItemViewType ,isEnabled 因为我们的item的样式不止一种,且为显示图片的那个Item让它可以点击。
最后就是图片展示的Activity:
package com.zhy.csdndemo; import android.app.Activity; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.widget.ProgressBar; import android.widget.Toast; import com.polites.android.GestureImageView; import com.zhy.csdndemo.util.FileUtil; import com.zhy.csdndemo.util.Http; public class ImageShowActivity extends Activity { private String url; private ProgressBar mLoading; private GestureImageView mGestureImageView; private Bitmap mBitmap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_image_page); // 拿到图片的链接 url = getIntent().getExtras().getString("url"); mLoading = (ProgressBar) findViewById(R.id.loading); mGestureImageView = (GestureImageView) findViewById(R.id.image); new DownloadImgTask().execute(); } /** * 点击返回按钮 * * @param view */ public void back(View view) { finish(); } /** * 点击下载按钮 * * @param view */ public void downloadImg(View view) { mGestureImageView.setDrawingCacheEnabled(true); if (FileUtil.writeSDcard(url, mGestureImageView.getDrawingCache())) { Toast.makeText(getApplicationContext(), "保存成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), "保存失败", Toast.LENGTH_SHORT).show(); } mGestureImageView.setDrawingCacheEnabled(false); } class DownloadImgTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { mBitmap = Http.HttpGetBmp(url); return null; } @Override protected void onPostExecute(Void result) { mGestureImageView.setImageBitmap(mBitmap); mLoading.setVisibility(View.GONE); super.onPostExecute(result); } } }好了,省略了一些辅助类的方法和布局文件。下面看下效果。
好了,上传文件限制2M,没办法录太多。
源码稍后贴出。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。