Erlo

基于Xamarin Android实现的简单的浏览器

2018-06-25 13:26:13 发布   595 浏览  
页面报错/反馈
收藏 点赞

  最近做了一个Android浏览器,当然功能比较简单,主要实现了自己想要的一些功能……现在有好多浏览器为什么还要自己写?当你使用的时候总有那么一些地方不如意,于是就想自己写一个。

  开发环境:Xamarin Android(非Forms)+联想机子(5.0)+荣耀机子(8.0)

  【开发目标】

  1、浏览器的基本功能,关联Http和Https(在另一个APP中打开网页时,可以弹出本应用)

  2、创建应用目录,用来存放离线网页文件

  3、可以离线保存网页(格式为mht)

  4、关联mht和mhtml格式的文件

  【涉及到的技术点】

  1、重写Activity中的OnBackPressed方法,实现webview回退和再按一次退出程序的功能

  2、重写Activity中的OnConfigurationChanged方法,实现横竖屏功能

  【webview相关技术点】

  1、开启一些常用的设置:JavaScriptEnabled、DomStorageEnabled(如果DomStorageEnabled不启用,网页中的下拉刷新和加载更多将不起作用;例子:百度首页加载新闻)

  2、重写WebViewClient中的ShouldOverrideUrlLoading方法,在点击打开网页中的链接时,用自己的webview中打开连接,而不是打开其他的浏览器

  3、重写WebChromeClient中的OnReceivedTitle和OnProgressChanged方法,分别获取页面标题(作为离线文件的名称)和加载进度

  4、采用事件的方式,通知主Activity关于页面加载开始、加载结束、标题、加载进度等的一些事情,进而更新UI(这里和Java的写法有些不同)

  5、页面加载进度条

  【悬浮按钮】1、全屏(退出)按钮  2、保存网页  3、扫描二维码(版本兼容问题尚未实现)

  【网址输入框】

  1、输入正确的网址之后点击输入法中的“前往”调转

  2、隐藏输入法

  以上列到的功能基本实现,最后在荣耀V10上测试时,其他的功能还好,就是在打开离线文件时也不报错,就是打不开……郁闷啊!最后查了一下也没有找到原因。这里说一下场景,以方便大神发现问题,希望大神不吝赐教。在我的联想手机上测试时发现本地文件路径是这样的:file:///storage/emulated/0/DDZMyBrowser/SavePages/1.mht  此时可以正常浏览,而V10中得到的路径是这样的,内部存储:content://com.huawei.hidisk.fileprovider/root/storage/emulated/0/DDZMyBrowser/SavePages/1.mht   SD卡:content://com.huawei.hidisk.fileprovider/root/storage/0ABF-6213/1.mht  这两个都打不开。我查询的结果,这路径应该是利用FileProvider生成的(7.0以上),哎,并非真正的android开发,并不太懂,一脸懵逼,不知道是不是因为这个原因……开始我还寄希望于将content://转为file:///格式的,但是都失败了,最后想想网上说的webview支持content://开头的啊,自己在输入框中手动修改为:file:///storage/emulated/0/DDZMyBrowser/SavePages/1.mht  发现是可以征程浏览的……

  上一下截图:

  1、应用首页

  2、再按一次退出程序

  3、横屏

  4、竖屏

  5、网页中的下拉刷新

  6、加载更多

  7、用自己的webview中打开连接,而不是打开其他的浏览器;进度条

  8、全屏

  9、离线保存

  10、关联MHT

  11、关联HTTP和HTTPS

   12、actionGo

   13、最后再来一张V10加载异常的图片

   去去去,传上去之后发现图片太大了,全是百度的图片……这事儿弄得

  最后在贴一下代码,记录一下

  CS代码:

  1 using Android.App;
  2 using Android.Widget;
  3 using Android.OS;
  4 using Android.Webkit;
  5 using System;
  6 using Android.Support.Design.Widget;
  7 using Android.Content;
  8 using Android.Views;
  9 using Java.IO;
 10 using Android.Views.InputMethods;
 11 using Android.Content.PM;
 12 using Android.Content.Res;
 13 using Android.Provider;
 14 using Android.Database;
 15 
 16 namespace DDZ.MyBrowser
 17 {
 18     /// 
 19     /// 获取网页Title
 20     /// 
 21     /// 
 22     public delegate void MyWebViewTitleDelegate(string title);
 23 
 24     /// 
 25     /// 获取网页加载进度
 26     /// 
 27     public delegate void MyWebViewProgressChangedDelegate(int newProgress);
 28 
 29     /// 
 30     /// 网页加载完成事件
 31     /// 
 32     public delegate void MyWebViewPageFinishedDelegate();
 33 
 34     [IntentFilter(
 35         new[] { Intent.ActionView },
 36         Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
 37         DataSchemes = new[] { "http", "https" })]
 38     [IntentFilter(
 39         new[] { Intent.ActionView },
 40         Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
 41         DataSchemes = new[] { "file", "content" }, DataMimeType = "*/*", DataHost = "*", DataPathPattern = ".*\.mhtml")]
 42     [IntentFilter(
 43         new[] { Intent.ActionView },
 44         Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
 45         DataMimeType = "*/*", DataSchemes = new[] { "file", "content" }, DataHost = "*", DataPathPattern = ".*\.mht")]
 46     [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true,
 47         ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.KeyboardHidden)]
 48     public class MainActivity : Activity
 49     {
 50         WebView myBrowser;
 51         EditText edtTxtUrl;
 52         FloatingActionButton fabMain;
 53         FloatingActionButton fabSubQRcodeScan;
 54         FloatingActionButton fabSubToggleFullScreen;
 55         FloatingActionButton fabSubSaveMHT;
 56         ProgressBar myBrowserPBar;
 57 
 58         private static bool isFabOpen;
 59         private static bool isFullScreen;
 60         private static DateTime lastClickGoBack = DateTime.Now;
 61 
 62         private static string currentPageTitle;
 63         private readonly string externalStorageDirPath = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
 64         private readonly string selfFolderName = "DDZMyBrowser";
 65         private static string selfApplicationDirPath;
 66         protected override void OnCreate(Bundle savedInstanceState)
 67         {
 68             // https://blog.csdn.net/niunan/article/details/71774292
 69             base.OnCreate(savedInstanceState);
 70             // Set our view from the "main" layout resource
 71             SetContentView(Resource.Layout.activity_main);
 72 
 73             //  1、浏览器控件相关
 74             myBrowser = FindViewById(Resource.Id.myBrowser);
 75             //  要与Javascript交互,则webview必须设置支持Javascript
 76             myBrowser.Settings.JavaScriptEnabled = true;
 77             //  支持通过JS打开新窗口 
 78             myBrowser.Settings.JavaScriptCanOpenWindowsAutomatically = true;
 79             myBrowser.Settings.DomStorageEnabled = true;
 80             myBrowser.Settings.AllowFileAccessFromFileURLs = true;
 81 
 82             var myWebViewClient = new MyWebViewClient();
 83             myWebViewClient.GetWebViewPageFinishedDelegate += MyWebViewClient_GetWebViewPageFinishedDelegate;
 84             myBrowser.SetWebViewClient(myWebViewClient);
 85             var myWebChromeClient = new MyWebChromeClient();
 86             myWebChromeClient.GetWebViewTitleDelegate += MyWebChromeClient_GetWebViewTitleDelegate;
 87             myWebChromeClient.GetWebViewProgressChangedDelegate += MyWebChromeClient_GetWebViewProgressChangedDelegate;
 88             myBrowser.SetWebChromeClient(myWebChromeClient);
 89             edtTxtUrl = FindViewById(Resource.Id.edtTxtUrl);
 90             edtTxtUrl.EditorAction += EdtTxtUrl_EditorAction;
 91             myBrowserPBar = FindViewById(Resource.Id.myBrowserPBar);
 92 
 93             //  2、右下方悬浮控件
 94             fabMain = FindViewById(Resource.Id.fabMain);
 95             fabSubQRcodeScan = FindViewById(Resource.Id.fabSubQRcodeScan);
 96             fabSubToggleFullScreen = FindViewById(Resource.Id.fabSubToggleFullScreen);
 97             fabSubSaveMHT = FindViewById(Resource.Id.fabSubSaveMHT);
 98             fabMain.Click += FabMain_Click;
 99             fabSubQRcodeScan.Click += FabSubQRcodeScan_Click;
100             fabSubToggleFullScreen.Click += FabSubToggleFullScreen_Click; ;
101             fabSubSaveMHT.Click += FabSubSaveMHT_Click;
102 
103             //  3、第三方应用使用该应用打开网页时,"this.Intent.DataString" 获取需要打开的网址
104             //      自己打开时,"this.Intent.DataString" 的值为空
105             String url = this.Intent.DataString;
106             if (!String.IsNullOrEmpty(url))
107             {              
108                 if (this.Intent.Data.Scheme == "content")
109                 {
110                     //  DocumentsContract.IsDocumentUri(this, this.Intent.Data):false
111 
112                     //  this.Intent.Data.Authority:com.huawei.hidisk.fileprovider
113                     //  this.Intent.Data.Host:com.huawei.hidisk.fileprovider
114                     //  this.Intent.Data.Path:/root/storage/0ABF-6213/xxx.mht
115                     //  this.Intent.Data.PathSegments:this.Intent.Data.Path的数组形式
116 
117                     //  Android.Support.V4.Content.FileProvider.GetUriForFile()
118                     //   this.Intent.SetFlags(ActivityFlags.GrantReadUriPermission).SetFlags(ActivityFlags.GrantWriteUriPermission);
119                 }
120             }
121             edtTxtUrl.Text = url;
122             LoadOnePage(url);
123 
124             //  4、创建应用目录
125             CreateSelfApplicationFolder();
126         }       
127 
128         private void EdtTxtUrl_EditorAction(object sender, TextView.EditorActionEventArgs e)
129         {
130             string inputUrl = edtTxtUrl.Text.Trim();
131             if (e.ActionId == ImeAction.Go)
132             {
133                 HideSoftInputFn();
134                 LoadOnePage(inputUrl);
135             }
136         }
137 
138         #region 获取WebView加载页面相关信息的一些自定义事件
139         private void MyWebViewClient_GetWebViewPageFinishedDelegate()
140         {
141             Toast.MakeText(this, "加载完成", ToastLength.Long).Show();
142         }
143 
144         private void MyWebChromeClient_GetWebViewProgressChangedDelegate(int newProgress)
145         {
146             myBrowserPBar.Visibility = ViewStates.Visible;
147             myBrowserPBar.Progress = newProgress;
148             if (newProgress == 100)
149             {
150                 myBrowserPBar.Visibility = ViewStates.Gone;
151             }
152         }
153 
154         private void MyWebChromeClient_GetWebViewTitleDelegate(string title)
155         {
156             currentPageTitle = title;
157         }
158         #endregion
159 
160         #region 悬浮按钮
161         private void FabMain_Click(object sender, EventArgs e)
162         {
163             if (!isFabOpen)
164             {
165                 HideSoftInputFn();
166                 ShowFabMenu();
167             }
168             else
169             {
170                 CloseFabMenu();
171             }
172             SetToggleFullScreenBtnImg();
173         }
174 
175         private void FabSubQRcodeScan_Click(object sender, EventArgs e)
176         {
177             Toast.MakeText(this, "扫描二维码", ToastLength.Long).Show();
178         }
179 
180         private void FabSubSaveMHT_Click(object sender, EventArgs e)
181         {
182             string savePageDirPath = $"{selfApplicationDirPath}{File.Separator}SavePages";
183             File dir = new File(savePageDirPath);
184             if (!dir.Exists())
185             {
186                 bool retBool = dir.Mkdir();
187             }
188             myBrowser.SaveWebArchive($"{savePageDirPath}{File.Separator}{currentPageTitle}.mht");
189         }
190 
191         private void FabSubToggleFullScreen_Click(object sender, EventArgs e)
192         {
193             if (isFullScreen)
194             {   //  目前为全屏状态,修改为非全屏
195                 edtTxtUrl.Visibility = ViewStates.Visible;
196                 this.Window.ClearFlags(WindowManagerFlags.Fullscreen);
197             }
198             else
199             {   //  目前为非全屏状态,修改为全屏
200                 edtTxtUrl.Visibility = ViewStates.Gone;
201                 this.Window.SetFlags(WindowManagerFlags.Fullscreen, WindowManagerFlags.Fullscreen);
202             }
203             isFullScreen = !isFullScreen;
204             SetToggleFullScreenBtnImg();
205         }
206 
207         private void ShowFabMenu()
208         {
209             isFabOpen = true;
210             fabSubQRcodeScan.Visibility = ViewStates.Visible;
211             fabSubToggleFullScreen.Visibility = ViewStates.Visible;
212             fabSubSaveMHT.Visibility = ViewStates.Visible;
213 
214             fabMain.Animate().Rotation(135f);
215             fabSubQRcodeScan.Animate()
216                .TranslationY(-600f)
217                .Rotation(0f);
218             fabSubToggleFullScreen.Animate()
219                .TranslationY(-410f)
220                .Rotation(0f);
221             fabSubSaveMHT.Animate()
222                 .TranslationY(-220f)
223                 .Rotation(0f);
224         }
225 
226         private void CloseFabMenu()
227         {
228             isFabOpen = false;
229 
230             fabMain.Animate().Rotation(0f);
231             fabSubQRcodeScan.Animate()
232                 .TranslationY(0f)
233                 .Rotation(90f);
234             fabSubToggleFullScreen.Animate()
235                 .TranslationY(0f)
236                 .Rotation(90f);
237             fabSubSaveMHT.Animate()
238                 .TranslationY(0f)
239                 .Rotation(90f);
240 
241             fabSubQRcodeScan.Visibility = ViewStates.Gone;
242             fabSubToggleFullScreen.Visibility = ViewStates.Gone;
243             fabSubSaveMHT.Visibility = ViewStates.Gone;
244         }
245 
246         private void SetToggleFullScreenBtnImg()
247         {
248             if (isFullScreen)
249             {
250                 fabSubToggleFullScreen.SetImageResource(Resource.Drawable.fullscreenExit);
251             }
252             else
253             {
254                 fabSubToggleFullScreen.SetImageResource(Resource.Drawable.fullscreen);
255             }
256         }
257         #endregion
258 
259         #region 重写基类 Activity 方法
260         public override void OnConfigurationChanged(Configuration newConfig)
261         {
262             base.OnConfigurationChanged(newConfig);
263             if (newConfig.Orientation == Android.Content.Res.Orientation.Portrait)
264             {
265                 edtTxtUrl.Visibility = ViewStates.Visible;
266                 fabMain.Visibility = ViewStates.Visible;
267                 this.Window.ClearFlags(WindowManagerFlags.Fullscreen);
268                 isFullScreen = false;
269                 Toast.MakeText(Application.Context, "竖屏模式!", ToastLength.Long).Show();
270             }
271             else
272             {
273                 CloseFabMenu();
274                 edtTxtUrl.Visibility = ViewStates.Gone;
275        
登录查看全部

参与评论

评论留言

还没有评论留言,赶紧来抢楼吧~~

手机查看

返回顶部

给这篇文章打个标签吧~

棒极了 糟糕透顶 好文章 PHP JAVA JS 小程序 Python SEO MySql 确认