实现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,没办法录太多。



源码稍后贴出。





实现app上对csdn的文章查看,以及文章中图片的保存 (制作csdn app 完结篇),,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。