转载请标明出处: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. 关于Jquery中ajax方法data参数用法的总结

    data 发送到服务器的数据.将自动转换为请求字符串格式.GET 请求中将附加在 URL 后.查看 processData 选项说明以禁止此自动转换.必须为 Key/Value 格式.如果为数组,jQ ...

  2. 体验Openstack--感觉这个比HADOOP还要基础啊

    当然,这两者应该是可以整到一起的..这个和ESX是什么关系叫?? 今天闲时慢慢按文档整了一个大概出来.. http://www.linuxidc.com/Linux/2013-01/78253p3.h ...

  3. loadrunner_Controller技巧_overlay

    在scenario运行期间,我们经常有类似于:总结Vu数变化,Tps 或者response time变化的趋势或者对比response time 和 tps,那么我们就用的到 Controller的图 ...

  4. ASP.NET 弹出对话框和页面之间传递值的经验总结

    今天碰到一个弹出对话框(PopUp dialog)的问题, 因该是个傻瓜问题, 但是还是让我研究了半天, 总结了一些前人经验, 拿出来跟大家分享一下! 在ASP.Net中页面之间的传值方法有很多,但是 ...

  5. Understanding Item Import and Debugging Problems with Item Import (Doc ID 268968.1)

    In this Document Purpose Details   Scenario 1: Testing the basic item import with minimum columns po ...

  6. 利用逻辑运算符?"三个数字比大小

    static void Main(string[] args)        {            int a, b, c;            while (true)            ...

  7. 【转】下载太慢?简单设置让iTunes提速十几倍

    原文网址:http://www.startos.com/mac/ipad/tips/2010120713291.html 今年可以说是苹果欢笑的一年,ipad的发布,iphone4的成功,让用苹果设备 ...

  8. 在User Profile Service中配置AD的同步连接

    转:http://www.360sps.com/Item/ConfigureSynchronizationConnections.aspx 如果要将Active Directory.LDAP 目录和业 ...

  9. 由点击页面其它地方隐藏div所想到的jQuery的delegate

    对于这个问题一般有两种思路,这两种思路都会利用事件冒泡这一原理,想要详细了解Javascript事件机制可以看看JavaScript与HTML交互——事件,这不是本文重点,所以这里只是简单介绍一下事件 ...

  10. Android What is Application

    本文转自:http://www.cnblogs.com/elleniou/archive/2012/05/16/2502661.html Application和Activity,Service一样是 ...