转载请标明出处: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);
} } /**
* 点击返回button
* @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(); } /**
* 点击返回button
*
* @param view
*/
public void back(View view)
{
finish();
} /**
* 点击下载button
*
* @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。没办法录太多。

源代码点击此处下载

实现app上对csdn的文章查看,以及文章中图片的保存 (制作csdn app 完结篇)的更多相关文章

  1. [iOS基础控件 - 5.2] 查看大图、缩放图片代码(UIScrollView制作)

    原图: 900 x 1305      拖曳滚动:   缩放:           主要代码: // // ViewController.m // ImageZoom // // Created by ...

  2. 实现app上对csdn的文章列表上拉刷新下拉加载以及加入缓存文章列表的功能 (制作csdn app 四)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/23698511 今天继续对我们的csdn客户端未完成的功能进行实现,本篇博客接着客 ...

  3. 客户端上显示csdn上的各类别下的的文章列表 (制作csdn app 三)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/23597229 今天将在Android 使用Fragment,ViewPagerI ...

  4. 抓取csdn上的各类别的文章 (制作csdn app 二)

    转载请表明出处:http://blog.csdn.net/lmj623565791/article/details/23532797 这篇博客接着上一篇(Android 使用Fragment,View ...

  5. 巨高兴,偶的文章 “如何在服务器上配置ODBC来访问本机DB2for Windows服务器”被推荐至CSDN博客首页

    非常高兴,偶的文章 "如何在服务器上配置ODBC来访问本机DB2for Windows服务器"被推荐至CSDN博客首页,截图留念.                  文章被推荐在C ...

  6. “杀死” App 上的疑难崩溃

    在移动应用性能方面,崩溃带来的影响是最为严重的,程序崩了可以打断用户正在进行的操作体验,造成关键业务中断.用户留存率下降.品牌口碑变差.生命周期价值下降等影响.很多公司将崩溃率作为优先级最高的技术指标 ...

  7. 关于app上页面和js的调试

    不久前折腾了一晚上关于app上的页面和js的调试: 首先得准备几个比较比较常用的工具: 1.chrome(这个都没有你还干啥fe呀) 2.Fiddler(抓包神器,基本也是必备咯) 3.sublime ...

  8. Python+爬虫+xlwings发现CSDN个人博客热门文章

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 最近几天老猿博客的访问量出现了比较大的增长,从常规的1000-3000之间波动的范围一下子翻了将近一倍,粉丝增长从日均10-40人也增长了差不多一倍 ...

  9. APP上传

    原文网址: http://blog.csdn.net/ayangcool 前言:作为一名IOS开发者,把开发出来的App上传到App Store是必须的.下面就来详细介绍下具体流程. 1.打开苹果开发 ...

随机推荐

  1. CF192div2-330B - Road Construction

    题意:给定n个城市并建造马路是的两两到达,且距离不能超过2 因为0<=m<n/2,所以必然存在某个城市是无限制的,那就可以以这个无限制的城市为中心建造.... 只要想通了真尼玛简单.... ...

  2. Foundation和UIKit框架图

    学习Foundation和UIKit的时候比较容易忽视的一个问题: 对于一个新的类,知道它的用法和属性方法,但往往忽视了它的继承关系, 了解类的继承关系能帮助加深对其理解. 另外在官方文档中每一个类的 ...

  3. push 栈顶sp=sp-2 可以把立着的栈,向左侧倒下,那么形态就和反汇编时,内存的形态是一样的。小偏移的字节在前, 大的偏移字节在后

    push  栈顶sp=sp-2 可以把立着的栈,向左侧倒下,那么形态就和反汇编时,内存的形态是一样的.小偏移的字节在前, 大的偏移字节在后. 1 2 3 4 5 1 2 3 4 5

  4. 知识总结: Activity的四种启动模式

    通常情况下,一个应用有一个Task,这个Task就是为了完成某个工作的一系列Activity的集合.而这些Activity又被组织成了堆栈的形式.当一个Activity启动时,就会把它压入该Task的 ...

  5. UC何小鹏:移动互联网创业需警惕五大“不靠谱

    http://tech.qq.com/a/20140121/012443.htm 腾讯科技 启言 1月21日报道 移动互联网创业很容易犯错误,一不小心就陷入“坑”中.UC也是如此.近日,UC创始人何小 ...

  6. Ember.js demo3

    <!DOCTYPE html> <html> <head> <script src="http://code.jquery.com/jquery.j ...

  7. Java-Swing嵌入浏览器(二)

    这是qtjambi利用webview来做嵌入式浏览器,下面是我的工程目录. 运行效果如下图: 代码相关: package qtBowers; import com.trolltech.qt.core. ...

  8. Wzplayer C++ 版本,WzplayerPro

    WzplayerPro 是Wzplayer的C++版本,新版本支持插件解码器加载等等功能,以上是预览界面. 将会与Wzplayer一样,支持更多的平台,而且WzPlayer在初始化方面将会更快. 联系 ...

  9. 错误信息:A TCP error (10013: 以一种访问权限不允许的方式做了一个访问套接字的尝试。) occurred while listening on IP Endpoint=192.168.1.18:8002.

    百度之后,知道这个原因是端口已经被其他进程打开了 使用cmd命令,查看8002端口被哪一个程序占用了 C:\Windows\System32>netstat -ano | find " ...

  10. WCF - Hosting WCF Service

    After creating a WCF service, the next step is to host it so that the client applications can consum ...