`
jgsj
  • 浏览: 1028249 次
文章分类
社区版块
存档分类
最新评论

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



源码点击此处下载





分享到:
评论

相关推荐

    制作csdn app 四

    【标题】"制作csdn app 四"涉及的是一篇关于如何开发CSDN(China Software Developer Network)移动应用的文章,作者通过实例分享了在博客中的详细教程。在这个过程中,作者可能涵盖了移动应用开发的基本概念,以及...

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

    在CSDN App中,如果文章列表包含图片预览,这个库可以帮助提供更好的用户体验,让用户可以自由地查看文章配图。 `XListView`是一个可滚动的列表视图,它支持下拉刷新和上拉加载更多功能,这对于一个显示大量文章...

    CSDN app(完)

    【CSDN app】是一个基于Android平台的应用程序,主要用于浏览和互动CSDN(China Software Developer Network)上的博客和技术文章。这个应用集成了多种功能,包括但不限于阅读、搜索、评论和分享,旨在方便开发者们...

    (APP制作_APP开发平台_APP教程

    这篇文章我将介绍几个适用于站长的 App 制 作工具,顺便介绍下他们各自的特点。即使不会 代码也可以搞定,做出自己的 App。

    Android CSDN APP粗略模仿

    在模仿CSDN APP的过程中,Xutils可能被用于处理网络请求,如获取用户信息、文章列表等。 2. **AFinal**:AFinal是另一个Android开发中的开源框架,它主要解决了Android中的数据库操作问题,简化了SQLite数据库的...

    基于App Inventor积木式编程API调用的设计与实现——以天气预报APP为例.pdf

    基于App Inventor积木式编程API调用的设计与实现——以天气预报APP为例 本文主要介绍了基于App Inventor积木式编程的设计与实现,以天气预报APP为例,旨在提高青少年学习积木式编程的兴趣。App Inventor积木式编程...

    WhatsApp v0.2.6426 电脑版.zip

    ★ 离线信息:即使您错过了推送通知或关闭了您的手机,WhatsApp也会保存您在离线时接收到的信息,您在再次登陆本应用程序时便可以查看这些信息。 ★ 更多优点:您可以分享地址、交换联络人资料、设置个性化墙纸、...

    基于Android开发框架的新闻类APP的设计与实现.pdf

    文章首先对基于Android的国内外应用开发框架进行比较,然后对基于Android开发框架进行设计和实现,最后基于该开发框架设计并实现了一个新闻类APP,通过实例对开发框架的有效性进行验证。 在移动互联网时代,Android...

    Android app缓存清理实现

    在Android应用开发中,缓存管理是至关重要的一个环节,它关系到应用的性能、内存使用以及用户体验。本文将深入探讨如何实现Android app的缓存清理,并基于提供的"CacheClear"压缩包文件,讲解如何封装一个获取和清理...

    微信跳转到浏览器下载APP实现代码

    在移动互联网时代,微信作为一款超级App,拥有庞大的用户群体,很多开发者希望能在微信内部实现跳转到外部浏览器下载自己的应用程序。本知识点主要探讨如何通过编程实现“微信跳转到浏览器下载APP”的功能,这对于...

    APP开发制作完整流程.pdf

    APP开发制作完整流程 APP开发制作的完整流程是一个复杂的过程,涉及到团队组建、开发流程、测试、运营等多个方面。以下是该流程的详细说明: 一、团队组建 在APP开发制作的流程中,团队组建是一个至关重要的步骤...

    基于uni-app开发的新闻和资讯类App模板

    3. **组件使用**:uni-app提供了丰富的组件库,如轮播图、列表、网格、弹窗等,这些组件在模板中被广泛应用,以实现各种功能,如广告展示、文章分类、评论互动等。 4. **路由跳转**:uni-app的路由管理系统使得页面...

    CSDNApp_232.apk

    CSDNApp_232.apk

    CSDNApp_231.apk

    CSDNApp_231.apk

    基于Android 的同城急送APP的设计与实现.rar

    摘要:同城急送的APP使用的是Android这个开发环境,主要实现了用户对于代跑腿功能的使用以及管理,主要满足的是用户跟管理员的要求。同城急送的APP也有非常多的优点,其主要的优点是:界面设计简洁、操作设计的也...

    App inventor APP例程组件

    上一篇文章实现了App inventor接收STM32设备端上传阿里云的数据等。 这篇文章将实现App inventor控制STM32设备端用电器 6. MIT App inventor 自制手机APP连接阿里云平台 手机APP显示STM32发来的数据 所用到的工具...

    安卓期末大作业-记账app(含源码+导出app+运行截图)

    2023年上半年移动开发期末大作业记账app,老师给了95分,可以记录各种类型的账目支出记录,收入记录,存储到数据库中,可以隐藏账目记录,可以记录和删除记录,还可以将收支记录转图表分析,也可清空所有数据。

    基于HarmonyOS的物联网app开发,实现实时显示温湿度等数据,实现对设备开关的控制

    1、 源代码一(实现对设备开关的控制):通过调用阿里云API实现对设备属性参数的设置,进而达到控制设备开关的效果。由此可见,不仅仅可以用来控制设备开关,设备的所有属性都可以参考本代码实现对其的设计。 2、...

    淘宝app sign加密参数破解-CSDN博客 (2024_4_2 11_33_09).html

    淘宝app sign加密参数破解-CSDN博客 (2024_4_2 11_33_09).html

Global site tag (gtag.js) - Google Analytics