新闻详情页查看大图列表以及保存图片
来源:互联网 发布:淘宝客服需要会什么 编辑:程序博客网 时间:2024/05/22 06:56
最近项目中需要实现,点击新闻详情页查看大图列表并实现保存功能,今天写本篇博客总结梳理一下,一方面对知识点加深印象,
另一方面希望能对有需要的朋友提供些许帮助,如下图,我们以新闻列表中的第二个条目为例进行说明。
该新闻条目路径:http://mini.eastday.com/mobile/170830155812023.html
该新闻条目源码:
<!DOCTYPE html><html lang="zh-cn"><head><meta charset="UTF-8"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" name="viewport"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="format-detection" content="telephone=no"><meta name="format-detection" content="email=no"><meta name="author" content="Cleam Lee"><meta name="keywords" content="东方头条,头条新闻,头条,今日新闻头条,头条网,头条新闻,今日头条新闻"><meta name="description" content="东方头条网-东方网旗下《东方头条》是一款会自动学习的资讯软件,它会分析你的兴趣爱好,为你推荐喜欢的内容,并且越用越懂你.就要你好看,东方头条新闻网!"><title>伊莎贝莉水中上演湿身诱惑 穿透视纱裙性感风骚</title><script type="text/javascript" src="https://mini.eastday.com/toutiaoh5/js/responsive.min.js"></script><link rel="stylesheet" href="https://mini.eastday.com/toutiaoh5/css/photoswipe/photoswipe.min.css"><link rel="stylesheet" href="https://mini.eastday.com/toutiaoh5/css/common.min.css"><link rel="stylesheet" href="https://mini.eastday.com/toutiaoh5/css/page_details_v4.min.css"></head><body><input type="hidden" value="yule" id="newstype"><input id="datetime_forapp" type="hidden" value="2017-08-30 15:58"><input id="uid_forapp" type="hidden" value="200000000006426"><input id="avatar_forapp" type="hidden" value="https://00.imgmini.eastday.com/dcminisite/portrait/84ab8437dbb57f6b4641f169da22d176.jpg"><input id="nickname_forapp" type="hidden" value="国际在线"><article id="J_article" class="J-article article"><div id="title"><div class="article-title"><h1 class="title">伊莎贝莉水中上演湿身诱惑 穿透视纱裙性感风骚</h1></div><div class="article-src-time"><span class="src">2017-08-30 15:58 来源:国际在线</span></div></div><div id="content" class="J-article-content article-content"><figure class="section img"><a class="img-wrap" style="padding-bottom: 147.55%;" href="https://02.imgmini.eastday.com/mobile/20170830/20170830155812_78b1c712dd30e212a7c052f9ca9b4868_1.jpeg" data-size="694x1024"><img width="100%" alt="" src="https://02.imgmini.eastday.com/mobile/20170830/20170830155812_78b1c712dd30e212a7c052f9ca9b4868_1.jpeg" data-weight="694" data-width="694" data-height="1024"></a></figure><p class="section txt">当地时间2017年8月29日,意大利威尼斯,伊莎贝莉-芳塔娜(Isabeli Fontana)第74届威尼斯电影节记者会。下海玩湿身,她身穿水墨色的透视纱裙大玩湿身诱惑,风情万种性感娇躯诱惑人心。David FisherREXShutterstock/东方IC</p><figure class="section img"><a class="img-wrap" style="padding-bottom: 136.90%;" href="https://02.imgmini.eastday.com/mobile/20170830/20170830155812_2a97d2c718656775a0bb5106ff11d0cf_2.jpeg" data-size="748x1024"><img width="100%" alt="" src="https://02.imgmini.eastday.com/mobile/20170830/20170830155812_2a97d2c718656775a0bb5106ff11d0cf_2.jpeg" data-weight="748" data-width="748" data-height="1024"></a></figure><p class="section txt">当地时间2017年8月29日,意大利威尼斯,伊莎贝莉-芳塔娜(Isabeli Fontana)第74届威尼斯电影节记者会。下海玩湿身,她身穿水墨色的透视纱裙大玩湿身诱惑,风情万种性感娇躯诱惑人心。David FisherREXShutterstock/东方IC</p><figure class="section img"><a class="img-wrap" style="padding-bottom: 60.44%;" href="https://02.imgmini.eastday.com/mobile/20170830/20170830155812_e3268fa157ae755b8a5ddb6c3217f867_3.jpeg" data-size="900x544"><img width="100%" alt="" src="https://02.imgmini.eastday.com/mobile/20170830/20170830155812_e3268fa157ae755b8a5ddb6c3217f867_3.jpeg" data-weight="900" data-width="900" data-height="544"></a></figure><p class="section txt">当地时间2017年8月29日,意大利威尼斯,伊莎贝莉-芳塔娜(Isabeli Fontana)第74届威尼斯电影节记者会。下海玩湿身,她身穿水墨色的透视纱裙大玩湿身诱惑,风情万种性感娇躯诱惑人心。David FisherREXShutterstock/东方IC</p></div></article><div id="news_check"><div id="J_interest_news" class="interest-news"></div><div id="J_hot_news" class="hot-news"></div></div><div class="pswp" tabindex="-1" role="dialog" aria-hidden="true"><div class="pswp__bg"></div><div class="pswp__scroll-wrap"><div class="pswp__container"><div class="pswp__item"></div><div class="pswp__item"></div><div class="pswp__item"></div></div><div class="pswp__ui pswp__ui--hidden"><div class="pswp__top-bar"><div class="pswp__counter"></div><div class="pswp__preloader"><div class="pswp__preloader__icn"><div class="pswp__preloader__cut"><div class="pswp__preloader__donut"></div></div></div></div></div><div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap"><div class="pswp__share-tooltip"></div></div><div class="pswp__caption"><div class="pswp__caption__center"></div></div></div></div></div><script src="https://mini.eastday.com/toutiaoh5/js/photoswipe/photoswipe.min.js"></script><script src="https://mini.eastday.com/toutiaoh5/js/common.min.js"></script><script src="https://mini.eastday.com/toutiaoh5/js/gg_details_v2.min.js"></script><script src="https://mini.eastday.com/toutiaoh5/js/page_details_v2.min.js"></script></body></html>
其中最为关键的是:
<a class="img-wrap" style="padding-bottom: 147.55%;" href="https://02.imgmini.eastday.com/mobile/20170830/20170830155812_78b1c712dd30e212a7c052f9ca9b4868_1.jpeg" data-size="694x1024"><img width="100%" alt="" src="https://02.imgmini.eastday.com/mobile/20170830/20170830155812_78b1c712dd30e212a7c052f9ca9b4868_1.jpeg" data-weight="694" data-width="694" data-height="1024"></a>
锚点<a></a>中包含<img>标签,点击img会跳转到href链接中,href中即是图片的链接,我们对跳转事件进行拦截即可拿到图片的URL。因此要查看大图列表,首先要拿到所有图片的URL,我们分为如下几步完成大图列表查看、保存功能:
一、根据新闻URL链接,获取HTML源码
二、在HTML源码中获取所有的<img>标签,在<img>标签中找到<src>节点,从而获取图片的URL
三、点击图片跳转时,拦截URL
四、利用viewPager+PhotoView+Glide完成图片浏览
五、保存当前图片
下面我们分步骤实现,首先是获取HTML源码:
public static String getHtmlSourceCode(String path) { String sourceCode = null; try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); InputStream inStream = conn.getInputStream(); byte[] data = readInputStream(inStream); sourceCode = new String(data, "utf-8"); } catch (Exception e) { e.printStackTrace(); Log.i(TAG, "getHtmlSourceCode Exception"); } Log.i(TAG, "path:" + path); return sourceCode; } public static byte[] readInputStream(InputStream inStream) { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); try { byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outStream.write(buffer, 0, len); } inStream.close(); } catch (Exception e) { e.printStackTrace(); Log.i(TAG, "readInputStream Exception"); } return outStream.toByteArray(); }
获取HTML源码后,利用正则表达式,通过<img>标签以及其<src>节点找到所有图片的URL:
public static void getImagesUrlFromHtml(final String path) { new Thread() { @Override public void run() { List<String> imageSrcList = new ArrayList<String>(); String htmlCode = getHtmlSourceCode(path); //<img/>标签正则表达式 Pattern p = Pattern.compile("<img\\b[^>]*\\bsrc\\b\\s*=\\s*('|\")?([^'\"\n\r\f>]+(\\.jpg|\\.bmp|\\.eps|\\.gif|\\.mif|\\.miff|\\.png|\\.tif|\\.tiff|\\.svg|\\.wmf|\\.jpe|\\.jpeg|\\.dib|\\.ico|\\.tga|\\.cut|\\.pic)\\b)[^>]*>", Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(htmlCode); String quote = null; String src = null; while (m.find()) { quote = m.group(1); src = (quote == null || quote.trim().length() == 0) ? m.group(2).split("//s+")[0] : m.group(2); imageSrcList.add(src); } if (imageSrcList == null || imageSrcList.size() == 0) { Log.i(TAG, "新闻中未匹配到图片链接"); } EventBus.getDefault().post(new NewsPhotoUrlsEvent(imageSrcList.toArray(new String[imageSrcList.size()]))); } }.start(); }
注意:获取HTML源码以及遍历工作要在子线程中执行。
获取到所有图片的URL后,我们要对图片的跳转事件进行拦截。再点击图片后,WebView会进行跳转,跳转的URL即是被点击图片的URL。想要自己处理跳转事件,我们需要给WebView设置WebViewClient并重写shouldOverrideUrlLoading方法:
public class NewsWebViewClient extends WebViewClient { private static final String TAG = "NewsWebViewClient"; private boolean isPageFinished; private String[] mImagesUrl; private Context mContext; public NewsWebViewClient(Context context) { mContext = context; } public void setImagesUrl(String[] imagesUrl) { mImagesUrl = imagesUrl; } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); isPageFinished = true; } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); } /** * 点击图片时拦截URL * * @param view * @param url * @return true表明,针对点击请求的URL,不执行跳转,WebViewClient自己处理点击请求的URL */ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (isPageFinished) { if (mImagesUrl != null) { for (String imageUrl : mImagesUrl) { if (!TextUtils.isEmpty(imageUrl)) { if (imageUrl.equals(url)) { new PhotoBrowserDialog(mContext, imageUrl, mImagesUrl).show(); } } } } } return true; }}
mImagesUrl即是所有图片的链接,点击某个新闻图片时,shouldOverrideUrlLoading(WebView view, String url)会被回调,参数url即是要跳转的链接,也就是该图片的URL。
shouldOverrideUrlLoading返回true表明WebView不执行跳转,WebViewClient自己处理点击请求的URL,此处我们弹出图片浏览对话框。isPageFinished表示WebView是否加载完成,加载完成时,点击图片才能弹出图片浏览对话框。还要对mImagesUrl进行判null,mImagesUrl的获取是在子线程中(异步执行),如果WebView加载完成,mImagesUrl还没有获取到的话,会crash,所以要判null。
接下来利用ViewPager、PhotoView、Glide完成大图浏览。新闻中有多少图片,ViewPager中就包含多少PhotoView,每个PhotoView显示一张图片,并且全屏显示,可放大可缩小,点击PhotoView,大图浏览对话框消失。
public class PhotoBrowserDialog implements View.OnClickListener { private static final String TAG = "PhotoBrowserDialog"; private ViewPager mViewPager; private ImageView mLoading; private TextView mCurrentPhoto; private TextView mSaveBtn; private String mCurImageUrl; private String[] mImageUrls; private List<PhotoView> mPhotoViewList; private int mCurrentPosition = -1; private DialogView mDialogView; private Context mContext; private View.OnClickListener mOnClickListener; public PhotoBrowserDialog(Context context, String currentUrl, String[] imageUrls) { mContext = context; mCurImageUrl = currentUrl; mImageUrls = imageUrls; initData(); initView(); } private void initView() { View view = LayoutInflater.from(mContext).inflate(R.layout.dialog_photo_browser, null); mViewPager = (ViewPager) view.findViewById(R.id.view_pager_news_photo_browser); mViewPager.setAdapter(new NewsPhotoPagerAdapter(mPhotoViewList)); mViewPager.setCurrentItem(mCurrentPosition); mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { mCurrentPhoto.setText(position + 1 + "/" + mImageUrls.length); } @Override public void onPageScrollStateChanged(int state) { } }); mViewPager.setPageTransformer(true, new ZoomInTransform()); mLoading = (ImageView) view.findViewById(R.id.loading_news_photo); mCurrentPhoto = (TextView) view.findViewById(R.id.current_positon_photo); mCurrentPhoto.setText(mCurrentPosition + 1 + "/" + mImageUrls.length); mSaveBtn = (TextView) view.findViewById(R.id.save_phonto_btn); mSaveBtn.setOnClickListener(this); mDialogView = new DialogView(mContext, view); mDialogView.setFullScreen(true); mDialogView.setCancelable(true); mDialogView.setOnDialogDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { if (mOnClickListener != null) { mOnClickListener = null; } if (mDialogView != null) { mDialogView = null; } if (mPhotoViewList != null) { mPhotoViewList = null; } } }); } private void initData() { mOnClickListener = new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); } }; mPhotoViewList = new ArrayList<>(mImageUrls.length); for (int i = 0; i < mImageUrls.length; i++) { //确定当前图片的position if (mCurImageUrl.equals(mImageUrls[i])) { mCurrentPosition = i; } final PhotoView photoView = getPhotoView(); Glide.with(mContext).load(mImageUrls[i]).transition(DrawableTransitionOptions.withCrossFade()).into(new SimpleTarget<Drawable>() { @Override public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) { photoView.setImageDrawable(resource); } @Override public void onLoadFailed(Drawable errorDrawable) { } }); mPhotoViewList.add(photoView); Log.i(TAG, "imageUrl-->" + mImageUrls[i]); } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.save_phonto_btn: savePhoto(); break; } } private void savePhoto() { PhotoView currentPhotoView = mPhotoViewList.get(mViewPager.getCurrentItem()); BitmapDrawable bitmapDrawable = (BitmapDrawable) currentPhotoView.getDrawable(); FileUtils.savePhoto(mContext, bitmapDrawable.getBitmap(), new FileUtils.SaveResultCallback() { @Override public void onSavedSuccess() { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { //主线程更新UI ToastUtil.toastInCenter(mContext, R.string.news_detail_save_photo_toast_success); } }); } @Override public void onSavedFailed() { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { //主线程更新UI ToastUtil.toastInCenter(mContext, R.string.news_detail_save_photo_toast_failed); } }); } }); } public void show() { if (mDialogView != null) { mDialogView.showDialog(); } } public void dismiss() { if (mDialogView != null) { mDialogView.dismissDialog(); } } /** * 获取自适应的PhotoView,宽度填满屏幕,高度按比例填充 * * @return */ private PhotoView getPhotoView() { PhotoView photoView = new PhotoView(mContext); photoView.enable();//允许缩放 photoView.setMaxWidth(ViewGroup.LayoutParams.MATCH_PARENT); photoView.setMaxHeight(3 * ViewGroup.LayoutParams.MATCH_PARENT); photoView.setScaleType(ImageView.ScaleType.FIT_XY);//填充整个屏幕 photoView.setAdjustViewBounds(true);//填充时,保持宽高比例 photoView.setOnClickListener(mOnClickListener); return photoView; }}
这里我们重点说明以下几项:
一 、mCurrentPosition
mCurrentPosition 表示当前图片的位置,初始时通过NewsWebViewClient传递过来的mCurImageUrl,遍历mImageUrls得到;ViewPager滑动时,通过onPageSelected(int position)确定。
二、ViewPager滑动时动画
mViewPager.setPageTransformer(true, new ZoomInTransform());
三、获取自适应的PhotoView,宽度填满屏幕,高度按比例填充
通过getPhotoView()实现
四、PhotoView设置监听器,点击图片时,对话框消失
PhotoBrowserDialog的布局:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/photo_browser_dialog_bg"> <android.support.v4.view.ViewPager android:id="@+id/view_pager_news_photo_browser" android:layout_width="match_parent" android:layout_height="match_parent" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/current_positon_photo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_marginBottom="16dp" android:layout_marginLeft="16dp" android:padding="10dp" android:textColor="@color/white" android:textSize="14sp" /> <TextView android:id="@+id/save_phonto_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="16dp" android:layout_marginRight="16dp" android:padding="10dp" android:text="@string/news_detail_save_photo" android:textColor="@color/white" android:textSize="14sp" /> <ImageView android:id="@+id/loading_news_photo" android:layout_width="32dp" android:layout_height="32dp" android:layout_centerInParent="true" android:src="@drawable/news_photo_loading" android:visibility="gone" /> </RelativeLayout></FrameLayout>接下来就是最后一步,保存当前图片(压缩后)到系统相册中:
public static void savePhoto(final Context context, final Bitmap bmp, final SaveResultCallback saveResultCallback) { new Thread(new Runnable() { @Override public void run() { File appDir = new File(Environment.getExternalStorageDirectory(), "topNews"); if (!appDir.exists()) { appDir.mkdir(); } SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置以当前时间格式为图片名称 String fileName = df.format(new Date()) + ".JPEG"; File file = new File(appDir, fileName); try { FileOutputStream fos = new FileOutputStream(file); bmp.compress(Bitmap.CompressFormat.JPEG, 70, fos); fos.flush(); fos.close(); saveResultCallback.onSavedSuccess(); } catch (FileNotFoundException e) { saveResultCallback.onSavedFailed(); e.printStackTrace(); } catch (IOException e) { saveResultCallback.onSavedFailed(); e.printStackTrace(); } //保存图片后发送广播通知更新数据库 Uri uri = Uri.fromFile(file); context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri)); } }).start(); }
到这里我们已经实现了所有的功能,代码只贴出了关键的部分,项目源码:https://github.com/xiyy/TopNews,这是一款新闻客户端,并提供直播功能,个人独自开发完成,欢迎大家关注,谢谢!
后续:
1 也可通过执行JS代码,对图片点击事件进行拦截,实现该功能,参考:http://blog.csdn.net/ganshenml/article/details/55050983?ref=myread
2 PhotoView宽度填满屏幕,高度自适应,参考:http://www.cnblogs.com/bcbr/articles/4268276.html 、http://www.jianshu.com/p/c9424615e99d
3 保存的图片先压缩,再保存,关于有损压缩(JPEG)、无损压缩(png),参考:http://www.jianshu.com/p/e9e1db845c21
- 新闻详情页查看大图列表以及保存图片
- Android 实现WebView点击图片查看大图列表及图片保存
- Android 实现WebView点击图片查看大图列表及图片保存
- 查看图片列表,选中之后查看大图,可缩放滑动
- 广告轮播+大图相册查看用于商品详情页
- android点击查看大图(长按保存图片)
- 点击查看大图(长按保存图片)
- 仿京东详情页商品图片查看
- 点击图片,查看大图
- jQuery 查看图片大图
- Fragment实现左右新闻列表和详情
- 新闻客户端07 - 新闻详情页
- 新闻详情页 UIWebView 使用
- WKWebview点击图片查看大图
- js 图片 点击查看大图
- JavaScript和CSS实现详情图片显示大图特效
- 新闻详情页,scrollview view label 等控件的使用,以及页面布局
- 商品详情页中,当鼠标放到小图上时,改变大图的图片为当前小图的图片
- C/C++中计算程序运行时间
- Laravel 快速入门 ( 一 ) composer 介绍以及安装
- monkey测试
- 设计模式之:工厂模式
- 网页 报错 200 404 400 401 错误
- 新闻详情页查看大图列表以及保存图片
- keras学习笔记---keras安装出现的问题
- Hadoop: HDFS数据流分析
- Docker 初实践手札
- 一分钟了解"英语表达:时域上是相关or不相关的,频域上是高频or低频的"
- Maven项目集成cxf框架发布WebService
- POJ2318 TOYS(叉积,计算几何)
- File Permissions
- JMeter在NON GUI Mode下执行压力测试(Windows)