实验报告
课程名称 Android应用编程实验
实验名称:网页应用-WebView
实验目的:学习WebView的相关功能
实验仪器设备:电脑、Android Studio、Android手机
实验内容:
- 应用WebView对象浏览网页;
- 调用本地HTML网页文件的JavaScript;
- 用Android程序操纵JavaScript对话框;
WebKit是一个开源的浏览器引擎,WebKit内核具有非常好的网页解析机制,很多系统都使用WebKit做浏览器内核。在WebKit的API包中,最重要、最常用的类是Android.webKit. WebKit。WebView类是WebKit模块Java层的视图类,所有需要使用Web浏览功能的Android应用程序都要创建该视图对象,用于显示和处理请求的网络资源。
为实现基本的相机程序效果,使用最简单的线性布局,通过组件的嵌套生成需要的布局。首先指定最外层的线性布局对齐方式为垂直布局,在其中嵌套一个LinearLayout布局,之后再编写EditText组件用来输入网站网址,加入一个button组件用于点击确定。之后在外层的线性布局中加入WebView组件用来展示需要显示的网页,如图1.1所示。
在MainActivity.java文件中修改文件,以实现对程序的控制。首先如下图1.2所示实例化所需的对象。
在上图1.2中,分别实例化了用于网页浏览的“WebView”类的“webView”对象、用于点击确定的“Button”类的“openWebBtn”对象、用于编辑输入网址的“EditText”类的“editText”。
对构造函数进行修改,使其实现“关联布局文件和控制文件,设置按钮监听事件”的功能。如图1.3所示。
通过findViewById方法关联图1.1中的相关组件,并为“openWeb”设置监听事件,以实现在鼠标之后,显示网页界面的功能。
为按钮的监听事件编写 mClick 类,以实现拍照和退出功能。如图1.4所示:
上图中,构造了一个继承于OnClickListener的mClick类。首先定义一个url字符串,在点击按钮之后,通过getText().toString()方法获取文本框中的字符串,将其赋值给url。通过findViewById方法将webView对象与布局文件中的组件相关联,通过loadUrl方法设置网页的网址,从而显示网页。
由于程序中需要实现访问网页的功能,所以需要在程序中添加允许程序网络访问的权限。如下图1.5所示,在 AndroidManifest.xml 文件中添加下列信息,以申请权限。
编写程序结束之后,打开AndroidStudio中的安卓模拟器进行测试,但程序不能正常运行。
在之前的程序中,使用了webView.loadUrl () 方法去加载网页的内容。在安卓7.1.1系统的手机模拟器下,运行结果如图1.6所示:
在输入URL之后,点击“打开网页”按钮,会出现上图1.6所示的系统提示。该提示意思是,询问用户是以WebView Browser浏览器打开,还是以Chrome浏览器打开网页。
点击选择WebView Browser浏览器打开网页,则会显示如图1.7所示的效果。这样的话,系统会在程序之外调用WebView Browser浏览器来显示网页。
也就是说,用这种方式打开的网页资源是依赖于系统自带的浏览器,而不是WebView组件,若想用自身WebView组件去实现,需要去调用setWebViewClient()方法来实现。现在将代码修改,添加上setWebViewClient()方法。代码如下图1.8所示:
在setWebViewClient()方法中重载shouldOverrideUrlLoading,返回值若为true将用webview,false则是系统自身浏览器,现在将返回值改为true,使程序调用webview。
重新运行安卓模拟器,显示的效果如下图1.9所示:
现在可以看到,在手机界面的上半部分,是EditText和button组件,下半部分是webview组件。程序没有调用系统自带的浏览器,而是使用了内嵌的webview来浏览网页的。
现在已经可以实现不依靠系统自带浏览器打开我们的url资源,但这里会出现一个问题,只要我们一点手机上的返回键,整个程序就直接退出了。我们想要的应该是和浏览器一样的效果,即按下返回键应该还是向后退一步,回到之前浏览的网页,而不是直接退出程序。
解决方法,是去监听物理返回键并做出对应的相应事件,即重载onKeyDown函数,如图1.10所示:
上图中,重载了onKeyDown方法,具体含义为:keyCode代表按键的数字标识符,通过该标识符判断是否为返回键。如果是返回键,则判断目前的网页是否可以返回,如果可以返回,则调用goBack方法,返回网页前一页面;如果不能返回,则退出该程序。
Android 6.0 之前我们申请权限直接在配置文件中配置一下即可,但是6.0之后,谷歌官方将权限分为普通权限和危险权限。对于危险权限来说,我们就需要进行动态设置。经过查阅资料,发现访问互联网的权限并不属于危险权限,所以暂时不考虑动态申请了。
现在已经实现了实验所要求的功能,在Android7.1.1版本的模拟器中运行,并将运行过程录制的视频记录如下:http://47.95.13.239/Study/Android/show/ex6_1.mp4
WebSettings类用于对WebView对象的属性进行设置;WebViewClient类用于对WebView对象中各种事件的处理,通过重写该方法,可以对WebView对象在页面载入、资源载入、页面访问错误等情况发生时进行各种操作;WebChromeClient是辅助WebView处理JavaScript对话框、网页的标题、网页的图标、加载进度条等操作的类。用户可以在Android程序中调用本地的HTML网页文件的JavaScript。
本次布局使用相对布局,需要添加的组件仅仅是一个WebView组件,如图2.1所示。
在MainActivity.java文件中修改文件,以实现对程序的控制。首先如下图1.2所示实例化所需的对象。
在上图2.2中,分别实例化了用于网页浏览的“WebView”类的“webView”对象、用于线程间通信的“Handler”类的“handler”对象、用于处理JavaScript的alert对话框的“MWebChromeClient”类的“WebChromeClient”对象。
对构造函数进行修改,使其实现“关联布局文件和控制文件,设置WebView属性、加载本地HTML资源”的功能。如图2.3所示。
通过findViewById方法关联图2.1中的相关组件,通过WebSettings类设置WebView的相关属性。属性设置的相关含义见表2.1。
表2.1WebSettings类的方法
方法 功能
setAllowFileAccess(false) 设置在WebView内部是否允许访问文件,默认允许访问。
setJavaScriptEnabled(true) 设置支持JavaScript脚本
setBuiltInZoomControls(true) 设置支持缩放
setDefaultFontSize(24) 设置默认字体大小
之后通过addJavascriptInterface方法,设置JavaScript接口,实现Android与JavaScript的通信。通过loadUrl方法设置需要打开的HTML文件。
下面编写MObiect类,用来调用JavaScript,代码如下图2.4所示:
这里让我奇怪的是,android_show()这个函数没有被调用过,估计程序会出现问题。
编写MWebChromeClient类,用以在Android中响应JavaScript的调用,如图2.5所示:
为了实现该程序“调用本地HTML网页文件中的JavaScript”的功能,需要新建一个HTML文件,如图2.15所示,创建asset文件夹。之后在asset文件夹下,创建test.html网页文件。并在文件中写入相关代码,如图2.16所示。
编写代码完成,启动安卓模拟器进行测试。程序运行不正常,只显示了空白的页面,如图2.6所示:
首次启动模拟器运行时,正如上图2.6所示,程序不能正常运行,显示界面为空白。仔细阅读代码可以看到,在onCreate方法中,我已经使用了loadUrl方法来设置本地的HTML资源路径,如图2.7红框中的部分所示。为了确定是不是本地的HTML文本资源有问题,我修改了HTML文件,添加了如图2.8红框中的部分。
在HTML文件中添加该语句之后,如果程序能够正常访问本地HTML资源,应该会在手机界面显示hello段落,如果不能显示“hello”的话,说明本地HTML资源有问题。
下面运行模拟器进行测试,测试结果如下图2.9所示:
可以看到界面中显示了hello,说明程序确实已经访问到了本地的HTML文件。出现刚才的不显示现象,是因为Script脚本这方面出现了某些问题。
之后我注意到了之前看到的android_show()这个函数,如图2.10红框所示。它在整个程序中是没有被调用过的,我怀疑是这里出现了问题,于是阅读这段代码。
可以看到这段代码中,应该是用handler在Android和js之间传递了信息,通过loadUrl()方法,调用JavaScript中的show_alert()函数,以实现某些功能。但是再回到test.html文件中,发现根本就没有写show_alert () 函数,只写了一个名为addAll的function。我以为是程序错了,就将test.html文件中的addAll函数改名成了show_alert () 。运行程序发现,依然不显示文字。
最后通过与同学的交流,发现问题出现在test.html文件中,如图2.11红框中所示。
问题出现在红色箭头所指的位置。通过查阅资料发现,document.write是JavaScript中对document.open所开启的文档流操作的API方法,它能够直接在文档流中写入字符串,在本程序中,使用了带格式的文本字符串,也就可以将HTML格式的文本写入到文档流中。之前的错误在于,没有将作为字符串输入文档流,破坏了HTML文本标记语言的格式,导致文本不能正常显示。
现在运行程序,可以看到如下图2.12所示的界面。
虽然现在看来程序算是运行正常,但是回到编译器中看看代码,发现刚才的android_show()依然未被调用过。我怀疑android_show()函数并不没有起到调用JavaScript的功能,于是将该函数中的内容删除,只保留函数名,改成如下图2.13所示的内容。
重新在模拟器运行程序,结果程序正常显示,效果与图2.12一致。这说明android_show()函数确实没有起作用。经过查阅资料得知,该程序在JS调用Android的过程中,采用的是addJavascriptInterface()方法,通过该方法将 Java 对象和 JS 对象进行映射。程序中定义的MObject类,也就是一个JavaScript接口函数,其中最重要的就是写上“@JavascriptInterface”来表明它是一个JavaScript接口,同时加上这个也是因为安全问题。其他内容在本次程序中暂时没有用到。程序中如图2.14所示的代码,将MObjcet类的对象映射到js的test对象,再通过document.write方法将文本流输出到手机界面。
现在已经实现了实验所要求的功能,在Android7.1.1版本的模拟器中运行,运行过程录制的视频如下:http://47.95.13.239/Study/Android/show/ex6_2.mp4
通过addJavascriptInterface()方法实现Java 对象和 JS 对象进行映射,在script脚本标签中定义show_alert()函数,通过alert()方法描述警告框,并通过onJsAlert()方法进行显示处理。
由于例题6_3与例题6_2大段代码相似,其中“布局文件”和“控制文件”的代码与例题6_3相同,下面不再重复记录这两部分。
上图3.1中,同样是编写了一个
将编写完成的程序在安卓模拟器上运行,查看运行结果,如图3.2所示。
在界面的顶部输入测试字符串“zhanghoujin”,点击顶部右侧的“call Android”按钮进行提交,此时会在页面的底部出现一个Toast提示框,输出“Hello zhanghoujin”字样。
因为有了例题6_2的经验,在此次例题6_3中未出现明显的问题。
现在已经实现了实验所要求的功能,在Android7.1.1版本的模拟器中运行,运行过程录制的视频如下:http://47.95.13.239/Study/Android/show/ex6_3.mp4
我已经将整理过的该项目的源代码上传到了GitHub:
https://github.com/ZHJ0125/AndroidLeaning
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_gravity="center_horizontal"
android:gravity="center">
<LinearLayout
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:layout_width="270dp"
android:layout_height="wrap_content"
android:id="@+id/editText1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button1"
android:width="1dp"
android:text="打开网页"/>
LinearLayout>
<WebView
android:id="@+id/webView1"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
LinearLayout>
package zhj.com.ex6_1;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends Activity {
WebView webView;
Button openWebBtn;
EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
openWebBtn = (Button) findViewById(R.id.button1);
editText = (EditText)findViewById(R.id.editText1);
openWebBtn.setOnClickListener(new mClick());
}
class mClick implements OnClickListener{
@Override
public void onClick(View v) {
String url = editText.getText().toString();
webView = (WebView)findViewById(R.id.webView1);
webView.setWebViewClient(new WebViewClient() {
/**
* 重写shouldOverrideUrlLoading,返回值若为true将用webview,false则是系统自身浏览器
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
webView.loadUrl("http://" + url);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (webView.canGoBack()) {
webView.goBack();
return true;
} else {
System.exit(0);
}
}
return super.onKeyDown(keyCode, event);
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="zhj.com.ex6_1">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
application>
<uses-permission android:name="android.permission.INTERNET"/>
manifest>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:id="@+id/hello"/>
<WebView
android:id="@+id/webView1"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
LinearLayout>
### 4.2.2 MainActivity.java
```java
package zhj.com.ex6_2;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.JavascriptInterface;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
WebView webView;
Handler handler = new Handler();
TextView textView;
MWebChromeClient mWebChromeClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = (WebView)findViewById(R.id.webView1);
textView = (TextView) findViewById(R.id.hello);
WebSettings webSettings = webView.getSettings();
webSettings.setAllowFileAccess(true);
webSettings.setJavaScriptEnabled(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setDefaultFontSize(24);
MObject mObject = new MObject();
webView.addJavascriptInterface(mObject, "test");
mWebChromeClient = new MWebChromeClient();
webView.setWebChromeClient(mWebChromeClient);
webView.loadUrl("file:///android_asset/test.html");
}
class MObject extends Object{
@JavascriptInterface
public void android_show(){
}
}
class MWebChromeClient extends WebChromeClient{
@Override
public boolean onJsAlert (WebView view, String url,String message, JsResult result){
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
return true;
}
}
}
<html>
<head>
<title>一个简单的JavaScript例程title>
head>
<body>
<script language="javascript" type="text/javascript">
function addAll(a,b,c){
return a+b+c;
}
var total = addAll(30,40,50);
var str = "Ran 5 hours,
finally finished the ";
document.write(" "+ str + total + " km! ");
script>
body>
html>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hello World!"
android:id="@+id/webView1"/>
LinearLayout>
package zhj.com.ex6_3;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.webkit.JavascriptInterface;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Toast;
public class MainActivity extends Activity {
WebView webView;
Handler handler = new Handler();
MWebChromeClient mWebChromeClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = (WebView)findViewById(R.id.webView1);
WebSettings webSettings = webView.getSettings();
webSettings.setAllowFileAccess(true);
webSettings.setJavaScriptEnabled(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setDefaultFontSize(24);
MObject mObject = new MObject();
webView.addJavascriptInterface(mObject, "test");
mWebChromeClient = new MWebChromeClient();
webView.setWebChromeClient(mWebChromeClient);
webView.loadUrl("file:///android_asset/test.html");
}
class MObject extends Object{
@JavascriptInterface
public void android_show(){
handler.post(new Runnable() {
@Override
public void run() {
System.out.println("提示:使用了多线程的run()方法!!");
webView.loadUrl("javascript: show_alert()");
}
});
}
}
class MWebChromeClient extends WebChromeClient{
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
return true;
}
}
}
<html>
<head>
<title>JavaScript与Android交互title>
head>
<script type="text/javascript">
function show_alert(){
var a = document.getElementById("text").value;
alert("Hello " + a);
}
script>
<body>
<form action="">
<input type="text" id="text" value=""/>
<input type="button" id="button" onclick="window.test.android_show()" value="call Android"/>
form>
body>
html>
这次安卓实验不太顺利,在例题6_2上花了一些时间,但最后问题都解决了。通过这次实验我意识到,只有把每个细节都搞明白,才能更好地避免错误的发生。虽然这次实验中出现了很多问题,但通过解决问题,我也收获了很多编程方面的知识。
在Android编程方面,主要掌握了以下知识: