Android實(shí)現(xiàn)點(diǎn)擊WebView界面中圖片滑動(dòng)瀏覽與保存圖片功能
1、找一個(gè)比較出名的客戶端有類似功能的,然后 Google 搜索,仿 XXXX,先粗略看一下有沒(méi)有現(xiàn)成的 Demo 可以參考,比如我這個(gè)需要,先去搜索一下 ”Android 仿微信朋友圈瀏覽圖片效果“ (這個(gè)搜索關(guān)鍵字很關(guān)鍵?。?,可是筆者沒(méi)找到符合該需要的 Demo。
2、在第一個(gè)方案不好使的情況下,我們沒(méi)有了參考,那么咱們就得自己思考這個(gè)大概實(shí)現(xiàn)思路,然后把這個(gè)需求進(jìn)行拆分,逐一擊破。所以思考大概如下:
(1)要想展示圖片那么就得先拿到圖片,要拿到圖片只有兩種可能,第一種可能是 WebView 本身緩存了圖片,我們?nèi)ゾ彺嬷凶x取圖片進(jìn)行顯示,可是想一下,咱們?yōu)g覽微博看圖的時(shí)候如果沒(méi)有網(wǎng),這時(shí)候去點(diǎn)擊圖片那么圖片是加載不出來(lái)的,所以這種可能否定了;所以只有第二種可能就是點(diǎn)擊圖片的時(shí)候拿到該圖片對(duì)應(yīng)的 URL 網(wǎng)址,然后咱們自己去網(wǎng)絡(luò)加載圖片進(jìn)行顯示,所以這個(gè)點(diǎn)我們 Get 到了。
(2)要滑動(dòng)圖片進(jìn)行顯示下一張,那么就需要我們能拿到所有要顯示的圖片的 URL ,然后放到一個(gè)數(shù)組里面,每次滑動(dòng)就進(jìn)行加載一張圖片,那么也就是我們一次性拿到所有 WebView 包含圖片的 URL,這個(gè)就不是在點(diǎn)擊圖片的時(shí)候去獲取,而是在 WebView 加載完成后獲取到,這怎么能拿到?再想一下,WebView 進(jìn)行加載顯示的時(shí)候其實(shí)是加載 HTML(比如 Assets 目錄中的文件)文本的字符串,然后進(jìn)行渲染處理顯示出來(lái),所以 HTML文本文件里面包含了我們想要的圖片網(wǎng)址,大家看一下下面這張截圖就是一個(gè)帶圖片的 WebView 對(duì)應(yīng)加載的 HTML文本文件部分截圖,
其中標(biāo)簽 src 對(duì)象的內(nèi)容就是我們想要的圖片 URL,所以到這里我們就有了思路,我們先拿到 WebView 加載的 HTML 內(nèi)容,然后在從 HTML 里面提取我要想要的 URL。
(3)現(xiàn)在我們能拿到所有圖片對(duì)應(yīng)的 URL,那么滑動(dòng)圖片顯示下一張就簡(jiǎn)單了,我們直接用一個(gè) ViewPager 來(lái)實(shí)現(xiàn)滑動(dòng)加載圖片即可。
總結(jié)要實(shí)現(xiàn)這個(gè)需要我們需要做的工作有:
- 拿到 WebView 加載的 HTML 文本。
- 從 HTML 文本中提取所有圖片對(duì)應(yīng)的 URL。
- 處理 WebView 中圖片的點(diǎn)擊和長(zhǎng)按響應(yīng)事件。
- 用 ViewPager 來(lái)實(shí)現(xiàn)滑動(dòng)加載下一張圖片。
下面我們就按照以上幾個(gè)步驟來(lái)實(shí)現(xiàn)我們想要的功能。
二、主要內(nèi)容
2.1 獲取 WebView 頁(yè)面所有圖片對(duì)應(yīng)地址
2.1.1 解析 WebView 頁(yè)面加載的 HTML文本文件
定義供 JavaScript 調(diào)用的交互接口
/** *這個(gè)接口就是給 JavaScript 調(diào)用的,調(diào)用結(jié)果就是返回 HTML 文本, *然后 getAllImageUrlFromHtml(HTML) *從 HTML文件中提取頁(yè)面所有圖片對(duì)應(yīng)的地址對(duì)象 **/ private class InJavaScriptLocalObj { /** * 獲取 WebView 加載對(duì)應(yīng)的 HTML 文本 * @param HTML WebView 加載對(duì)應(yīng)的 HTML 文本 */ @android.webkit.JavascriptInterface public void showSource(String html) { //從 HTML 文件中提取頁(yè)面所有圖片對(duì)應(yīng)的地址對(duì)象 getAllImageUrlFromHtml(html); } }
WebView 開(kāi)啟 JavaScript 腳本執(zhí)行,調(diào)用 JavaScript 代碼
mWebView.getSettings().setJavaScriptEnabled(true); mWebView.addJavascriptInterface(new InJavaScriptLocalObj(), "local_obj"); mWebView.setWebViewClient(new WebViewClient() { // 網(wǎng)頁(yè)跳轉(zhuǎn) @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } // 網(wǎng)頁(yè)加載結(jié)束 @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); //解析 HTML parseHTML(view); } /** * Java 調(diào)取 js 代碼, * @param view WebView */ private void parseHTML(WebView view) { //這段 js 代碼是解析獲取到了 HTML 文本文件,然后調(diào)用本地定義的 Java 代碼返回 //解析出來(lái)的 HTML 文本文件 view.loadUrl("javascript:window.local_obj.showSource('<head>'+" + "document.getElementsByTagName('html')[0].innerHTML+'</head>');"); }
2.1.2 從獲取到的 HTML文本文件中提取頁(yè)面所有圖片對(duì)應(yīng)的地址對(duì)象
// 獲取 img 標(biāo)簽正則 private static final String IMAGE_URL_TAG = "<img.*src=(.*?)[^>]*?>"; // 獲取 src 路徑的正則 private static final String IMAGE_URL_CONTENT = "http:\"?(.*?)(\"|>|\\s+)"; /*** * 獲取頁(yè)面所有圖片對(duì)應(yīng)的地址對(duì)象, * 例如 <img src="http://sc1.hao123img.com/data/f44d0aab7bc35b8767de3c48706d429e" /> * @param HTML WebView 加載的 HTML 文本 * @return */ private List<String> getAllImageUrlFromHtml(String html) { Matcher matcher = Pattern.compile(IMAGE_URL_TAG).matcher(html); List<String> listImgUrl = new ArrayList<String>(); while (matcher.find()) { listImgUrl.add(matcher.group()); } //從圖片對(duì)應(yīng)的地址對(duì)象中解析出 src 標(biāo)簽對(duì)應(yīng)的內(nèi)容 getAllImageUrlFormSrcObject(listImgUrl); return listImgUrl; } /*** * 從圖片對(duì)應(yīng)的地址對(duì)象中解析出 src 標(biāo)簽對(duì)應(yīng)的內(nèi)容,即 url * 例如 "http://sc1.hao123img.com/data/f44d0aab7bc35b8767de3c48706d429e" * @param listImageUrl 圖片地址對(duì)象例如 : *<img src="http://sc1.hao123img.com/data/f44daab" /> */ private List<String> getAllImageUrlFormSrcObject(List<String> listImageUrl) { for (String image : listImageUrl) { Matcher matcher = Pattern.compile(IMAGE_URL_CONTENT).matcher(image); while (matcher.find()) { listImgSrc.add(matcher.group().substring(0, matcher.group().length() - 1)); } } return listImgSrc; }
到這里我們獲取到了 WebView 頁(yè)面中所有圖片對(duì)象對(duì)應(yīng)的 URL 地址,下面就還差一步,就是在點(diǎn)擊 WebView 界面的圖片時(shí)候去響應(yīng)點(diǎn)擊事件,然后把相應(yīng)的 URL 地址傳遞給 ViewPager 進(jìn)行顯示就齊活了。
2.2 響應(yīng) WebView 界面圖片的點(diǎn)擊事件
2.2.1定義供 JavaScript 調(diào)用的交互接口
// js 通信接口,定義供 JavaScript 調(diào)用的交互接口 private class MyJavascriptInterface { private Context context; public MyJavascriptInterface(Context context) { this.context = context; } /** * 點(diǎn)擊圖片啟動(dòng)新的 ShowImageFromWebActivity,并傳入點(diǎn)擊圖片對(duì)應(yīng)的 url * 和頁(yè)面所有圖片對(duì)應(yīng)的 url * @param url 點(diǎn)擊圖片對(duì)應(yīng)的 url */ @android.webkit.JavascriptInterface public void openImage(String url) { Intent intent = new Intent(); intent.putExtra("image", url); //listImgSrc 該參數(shù)為頁(yè)面所有圖片對(duì)應(yīng)的 url intent.putStringArrayListExtra(URL_ALL, (ArrayList<String>) listImgSrc); intent.setClass(context, ShowImageFromWebActivity.class); context.startActivity(intent); } }
2.2.2 WebView 開(kāi)啟 JavaScript 腳本執(zhí)行,調(diào)用 JavaScript 代碼
mWebView.getSettings().setJavaScriptEnabled(true); //載入 js mWebView.addJavascriptInterface(new MyJavascriptInterface(this), "imageListener"); mWebView.setWebViewClient(new WebViewClient() { // 網(wǎng)頁(yè)跳轉(zhuǎn) @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } // 網(wǎng)頁(yè)加載結(jié)束 @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); // web 頁(yè)面加載完成,添加圖片的點(diǎn)擊 js 函數(shù) addImageClickListener(); } /** * 注入 js 函數(shù),這段 js 函數(shù)的功能就是,遍歷所有的圖片,并添加 onclick 函數(shù), * 實(shí)現(xiàn)點(diǎn)擊事件, * 函數(shù)的功能是在圖片點(diǎn)擊的時(shí)候調(diào)用本地 java 接口并傳遞點(diǎn)擊圖片對(duì)應(yīng)的 url 過(guò)去 */ private void addImageClickListener() { mWebView.loadUrl("javascript:(function(){" + "var objs = document.getElementsByTagName(\"img\"); " + "for(var i=0;i<objs.length;i++) " + "{" + " objs[i].onclick=function() " + " { " + " window.imageListener.openImage(this.src); " + " } " + "}" + "})()"); }
到這里我們完成了前兩步,拿去到 WebView 界面圖片對(duì)應(yīng)的所有 URL 地址和響應(yīng) WebView 界面圖片的點(diǎn)擊事件,下面的事情就簡(jiǎn)單了,用 ViewPager 滑動(dòng)顯示每一張圖片,再我們進(jìn)行最后一步之前,我們?cè)賮?lái)實(shí)現(xiàn)一個(gè)功能就是長(zhǎng)按 WebView 界面圖片,彈出對(duì)話框來(lái),然后可以選擇保存圖片功能,代碼如下:
WebView 中圖片長(zhǎng)按點(diǎn)擊事件處理
//長(zhǎng)按點(diǎn)擊事件 mWebView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { //響應(yīng)長(zhǎng)按事件 responseWebLongClick(v); return false; } }); /** * 響應(yīng) WebView 長(zhǎng)按圖片的點(diǎn)擊事件 * @param v */ private void responseWebLongClick(View v) { if (v instanceof WebView) { WebView.HitTestResult result = ((WebView) v).getHitTestResult(); if (result != null) { int type = result.getType(); //判斷點(diǎn)擊類型如果是圖片 if (type == WebView.HitTestResult.IMAGE_TYPE || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { longClickUrl = result.getExtra(); //彈出對(duì)話框 showDialog(longClickUrl); } } } } /** * 長(zhǎng)按 WebView 中圖片彈出對(duì)話框,可以選擇保存圖片 * @param url 點(diǎn)擊圖片對(duì)應(yīng)的 url */ private void showDialog(final String url) { new ActionSheetDialog(this) .builder() .setCancelable(true) .setCanceledOnTouchOutside(true) .addSheetItem( "保存到相冊(cè)", ActionSheetDialog.SheetItemColor.Blue, new ActionSheetDialog.OnSheetItemClickListener() { @Override public void onClick(int which) { //下載圖片 downloadImage(url); } }).show(); }
2.3 ViewPager 滑動(dòng)顯示每一張圖片,PhotoView 實(shí)現(xiàn)自由縮放功能
由于這部分代碼比較簡(jiǎn)單,這里就直接貼出部分代碼,文章中所用的 Demo 代碼最終會(huì)上傳到 GitHub上,有興趣可以去瞧一瞧完整的代碼,這里簡(jiǎn)單介紹幾個(gè)類,ShowImageFromWebActivity.java 這個(gè)類內(nèi)部就包含一個(gè) ViewPager 和兩個(gè)按鈕, ViewPager 用來(lái)滑動(dòng)顯示每一張圖片,按鈕用來(lái)顯示滑動(dòng)的頁(yè)數(shù)和實(shí)現(xiàn)點(diǎn)擊保存圖片功能,代碼如下:
public class ShowImageFromWebActivity extends Activity implements View.OnClickListener { private ViewPager vpImageBrowser; private TextView tvImageIndex;//顯示滑動(dòng)頁(yè)數(shù) private Button btnSave;//保存圖片按鈕 private ImageBrowserAdapter adapter; private ArrayList<String> imgUrls;//WebView 頁(yè)面所有圖片 URL private String url;//WebView 頁(yè)面所有圖片中被點(diǎn)擊圖片對(duì)應(yīng) URL private int currentIndex;//標(biāo)記被滑動(dòng)圖片在所有圖片中的位置 private Handler mHandler;//異步發(fā)送消息 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_show_image_from_web); initView(); initListener(); initData(); } private void initView(){ vpImageBrowser = (ViewPager) findViewById(R.id.vp_image_browser); tvImageIndex = (TextView) findViewById(R.id.tv_image_index); btnSave = (Button) findViewById(R.id.btn_save); } private void initData(){ mHandler = new Handler(); imgUrls=getIntent().getStringArrayListExtra(MainActivity.URL_ALL); url=getIntent().getStringExtra("image"); //獲取被點(diǎn)擊圖片在所有圖片中的位置 int position=imgUrls.indexOf(url); adapter=new ImageBrowserAdapter(this,imgUrls); vpImageBrowser.setAdapter(adapter); final int size=imgUrls.size(); if(size > 1) { tvImageIndex.setVisibility(View.VISIBLE); tvImageIndex.setText((position+1) + "/" + size); } else { tvImageIndex.setVisibility(View.GONE); } vpImageBrowser.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int arg0) { currentIndex=arg0; int index = arg0 % size; tvImageIndex.setText((index+1) + "/" + size); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { // TODO Auto-generated method stub } @Override public void onPageScrollStateChanged(int arg0) { // TODO Auto-generated method stub } }); vpImageBrowser.setCurrentItem(position); } private void initListener(){ btnSave.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_save : Toast.makeText(getApplicationContext(), "開(kāi)始下載圖片", Toast.LENGTH_SHORT).show(); downloadImage(); break; } /** * 開(kāi)始下載圖片 */ private void downloadImage() { downloadAsync(imgUrls.get(currentIndex), Environment.getExternalStorageDirectory().getAbsolutePath() + "/ImagesFromWebView"); } }
ShowImageFromWebActivity.java 對(duì)應(yīng) xml 文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black" tools:context="activity.ShowImageFromWebActivity"> <view.PhotoViewViewPager android:id="@+id/vp_image_browser" android:layout_width="match_parent" android:layout_height="match_parent" > </view.PhotoViewViewPager> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:orientation="horizontal" android:padding="16dp" > <TextView android:textSize="18sp" android:id="@+id/tv_image_index" android:layout_width="56dp" android:layout_height="36dp" android:background="@drawable/shape_corner_rect_gray" android:gravity="center" android:paddingTop="3dp" android:paddingBottom="3dp" android:paddingRight="10dp" android:paddingLeft="10dp" android:text="1/9" android:textColor="@android:color/white" /> <View android:layout_width="0dp" android:layout_height="1dp" android:layout_weight="1" /> <Button android:id="@+id/btn_save" android:textSize="@dimen/_16sp" android:layout_width="56dp" android:layout_height="36dp" android:background="@drawable/shape_corner_rect_gray" android:gravity="center" android:padding="4dp" android:text="保存" android:textColor="@color/white" /> </LinearLayout> </RelativeLayout>
ImageBrowserAdapter.java 類代碼如下:
public class ImageBrowserAdapter extends PagerAdapter { private Activity context; private List<String> picUrls; public ImageBrowserAdapter(Activity context, ArrayList<String> picUrls) { this.context = context; this.picUrls = picUrls; } @Override public int getCount() { return picUrls.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public View instantiateItem(ViewGroup container, int position) { View view = View.inflate(context, R.layout.item_image_browser, null); ImageView iv_image_browser = (ImageView) view.findViewById(R.id.show_webimage_imageview); String picUrl = picUrls.get(position); final PhotoViewAttacher photoViewAttacher=new PhotoViewAttacher(iv_image_browser); photoViewAttacher.setScaleType(ImageView.ScaleType.FIT_CENTER); //顯示圖片 Glide.with(context). load(picUrl) .crossFade() .placeholder(R.drawable.avatar_default) .error(R.drawable.image_default_rect) .into(new GlideDrawableImageViewTarget(iv_image_browser){ @Override public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) { super.onResourceReady(resource, animation); photoViewAttacher.update(); } }); container.addView(view); return view; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); }
上面代碼也很簡(jiǎn)單,就是根據(jù) URL 來(lái)加載顯示圖片,然后利用 PhotoView 進(jìn)行縮放。
//ImageBrowserAdapter Item 布局文件 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <RelativeLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical"> <uk.co.senab.photoview.PhotoView android:id="@+id/pv_show_image" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" android:src="@drawable/image_default_rect" /> </RelativeLayout> </RelativeLayout>
以上為本次學(xué)習(xí)內(nèi)容,如有錯(cuò)誤還望指正,謝謝!
文章中 Demo 已經(jīng)上傳在 GitHub上,地址為ShowImageFromWebView,大家也可以通過(guò)本地下載
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)各位Android開(kāi)發(fā)者們能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Android RecycleView和線型布局制作聊天布局
大家好,本篇文章主要講的是Android RecycleView和線型布局制作聊天布局,感興趣的同學(xué)趕緊來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01Android 實(shí)時(shí)監(jiān)測(cè)(監(jiān)聽(tīng))網(wǎng)絡(luò)連接狀態(tài)變化
這篇文章主要介紹了Android 實(shí)時(shí)監(jiān)測(cè)(監(jiān)聽(tīng))網(wǎng)絡(luò)連接狀態(tài)變化的相關(guān)知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-06-06Android入門之使用SimpleAdapter實(shí)現(xiàn)復(fù)雜界面布局
這篇文章主要為大家詳細(xì)介紹了Android如何使用SimpleAdapter實(shí)現(xiàn)復(fù)雜的界面布局,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Android有一定的幫助,需要的可以參考一下2022-11-11Android微信自動(dòng)搶紅包插件優(yōu)化和實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了Android微信自動(dòng)搶紅包插件優(yōu)化和實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Android自定義WaveView實(shí)現(xiàn)波浪進(jìn)度效果
最近注意到百度外賣以及淘寶個(gè)人中心,都用到了類似水波起伏的效果,于是就參照網(wǎng)上的資料然后自己整改,自定義了一個(gè)waveView來(lái)實(shí)現(xiàn)這個(gè)效果,文中給出來(lái)詳細(xì)的實(shí)現(xiàn)原理及實(shí)例代碼,有需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。2017-01-01android socket聊天室功能實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了android socket聊天室功能實(shí)現(xiàn)方法,不單純是聊天室,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03基于Android XML解析與保存的實(shí)現(xiàn)
本篇文章小編為大家介紹,基于Android XML解析與保存的實(shí)現(xiàn)。需要的朋友參考下2013-04-04Android應(yīng)用中炫酷的橫向和環(huán)形進(jìn)度條的實(shí)例分享
這篇文章主要介紹了Android應(yīng)用中炫酷的橫向和圓形進(jìn)度條的實(shí)例分享,文中利用了一些GitHub上的插件進(jìn)行改寫,也是一片很好的二次開(kāi)發(fā)教學(xué),需要的朋友可以參考下2016-04-04android閃關(guān)燈的開(kāi)啟和關(guān)閉方法代碼實(shí)例
這篇文章主要介紹了android閃關(guān)燈的開(kāi)啟和關(guān)閉方法代碼實(shí)例,本文直接給出代碼和配置實(shí)例,需要的朋友可以參考下2015-05-05Android實(shí)現(xiàn)瘋狂連連看游戲之加載界面圖片和實(shí)現(xiàn)游戲Activity(四)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)瘋狂連連看游戲之加載界面圖片和實(shí)現(xiàn)游戲Activity,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03