Webview的几个关键方法要介绍一些:
谷歌官方文档是这么说的;
A WebView has several customization points where you can add your own behavior. These are:
- Creating and setting a
WebChromeClient
subclass. This class is called when something that might impact a browser UI happens, for instance, progress updates and JavaScript alerts are sent here (see Debugging Tasks). - Creating and setting a
WebViewClient
subclass. It will be called when things happen that impact the rendering of the content, eg, errors or form submissions. You can also intercept URL loading here (viashouldOverrideUrlLoading()
). - Modifying the
WebSettings
, such as enabling JavaScript withsetJavaScriptEnabled()
. - Injecting Java objects into the WebView using the
addJavascriptInterface(Object, String)
method. This method allows you to inject Java objects into a page's JavaScript context, so that they can be accessed by JavaScript in the page.
Here's a more complicated example, showing error handling, settings, and progress notification:
1 // Let's display the progress in the activity title bar, like the 2 // browser app does. 3 getWindow().requestFeature(Window.FEATURE_PROGRESS); 4 5 webview.getSettings().setJavaScriptEnabled(true); 6 7 final Activity activity = this; 8 webview.setWebChromeClient(new WebChromeClient() { 9 public void onProgressChanged(WebView view, int progress) { 10 // Activities and WebViews measure progress with different scales. 11 // The progress meter will automatically disappear when we reach 100% 12 activity.setProgress(progress * 1000); 13 } 14 }); 15 webview.setWebViewClient(new WebViewClient() { 16 public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { 17 Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show(); 18 } 19 }); 20 21 webview.loadUrl("http://developer.android.com/");
1 webView1.loadUrl("http://stormzhang.com");//这个就不说了 2 webView1.setWebViewClient(new WebViewClient() {//
必须记得,这个方法的创建的webviewclient的shouldOverrideUrlLoading必须实现,不然在一个网页
内中的链接会跳转到用本地的浏览器打开,而不是你自己的浏览器打开,使用其中的view.loadUrl(url)就可以实现
4 public boolean shouldOverrideUrlLoading(WebView view, String url) { 6 view.loadUrl(url); 7 8 return super.shouldOverrideUrlLoading(view, url); 9 } 10 11 @Override 12 public void onPageStarted(WebView view, String url, Bitmap favicon) { 13 // TODO Auto-generated method stub 14 super.onPageStarted(view, url, favicon); 15 16 } 17 18 @Override 19 public void onPageFinished(WebView view, String url) { 20 // TODO Auto-generated method stub 21 super.onPageFinished(view, url); 22 23 } 24 25 }); 26 27 webView1.setWebChromeClient(new WebChromeClient() { 28 @Override 29 public void onProgressChanged(WebView view, int newProgress) {//
意思就是网页在不停的加载,本方法就在不停的进行,newProgress就是实际得到的
进度,可以根据此回调函数来完成进度条的控制. 31 super.onProgressChanged(view, newProgress); 32 if(newProgress==100){ 33 progressBar.setVisibility(View.GONE);//加载完网页进度条消失 34 } 35 else{ 36 progressBar.setVisibility(View.VISIBLE);//开始加载网页时显示进度条 37 progressBar.setProgress(newProgress);//设置进度值 38 } 39 } 40 });
具体的例子如下:
主界面布局文件:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 tools:context=".MainActivity" 6 android:orientation="vertical" 7 > 8 <RelativeLayout 9 android:layout_width="match_parent" 10 android:layout_height="40dp" 11 android:background="#1B9A16" 12 13 /> 14 15 16 <ProgressBar 17 android:id="@+id/progressBar1" 18 style="?android:attr/progressBarStyleHorizontal" 19 android:layout_width="match_parent" 20 android:layout_height="3dip" 21 android:progressDrawable="@drawable/pg" 22 android:visibility="gone" 23 24 /> 25 26 <WebView 27 android:id="@+id/webview1" 28 android:layout_below="@id/progressBar1" 29 android:layout_width="match_parent" 30 android:layout_height="match_parent" /> 31 32 33 </LinearLayout>
MainActivity文件:
1 package com.example.webview; 2 3 import android.os.Bundle; 4 import android.app.Activity; 5 import android.transition.Visibility; 6 import android.view.KeyEvent; 7 import android.view.Menu; 8 import android.view.View; 9 import android.view.Window; 10 import android.webkit.WebChromeClient; 11 import android.webkit.WebSettings; 12 import android.webkit.WebView; 13 import android.webkit.WebViewClient; 14 import android.widget.ProgressBar; 15 16 public class MainActivity extends Activity { 17 18 private WebView webView; 19 private ProgressBar pg1; 20 @Override 21 protected void onCreate(Bundle savedInstanceState) { 22 super.onCreate(savedInstanceState); 23 requestWindowFeature(Window.FEATURE_NO_TITLE); 24 setContentView(R.layout.activity_main); 25 init(); 26 webView.loadUrl("http://www.baidu.com"); 27 } 28 29 private void init() { 30 // TODO 自动生成的方法存根 31 webView=(WebView) findViewById(R.id.webview1); 32 pg1=(ProgressBar) findViewById(R.id.progressBar1); 33 34 webView.setWebViewClient(new WebViewClient(){ 35 //覆写shouldOverrideUrlLoading实现内部显示网页 36 @Override 37 public boolean shouldOverrideUrlLoading(WebView view, String url) { 38 // TODO 自动生成的方法存根 39 view.loadUrl(url); 40 return true; 41 } 42 }); 43 WebSettings seting=webView.getSettings(); 44 seting.setJavaScriptEnabled(true);//设置webview支持javascript脚本 45 webView.setWebChromeClient(new WebChromeClient(){ 46 @Override 47 public void onProgressChanged(WebView view, int newProgress) { 48 // TODO 自动生成的方法存根 49 50 if(newProgress==100){ 51 pg1.setVisibility(View.GONE);//加载完网页进度条消失 52 } 53 else{ 54 pg1.setVisibility(View.VISIBLE);//开始加载网页时显示进度条 55 pg1.setProgress(newProgress);//设置进度值 56 } 57 58 } 59 }); 60 61 } 62 63 64 //设置返回键动作(防止按返回键直接退出程序) 65 @Override 66 public boolean onKeyDown(int keyCode, KeyEvent event) { 67 // TODO 自动生成的方法存根 68 if(keyCode==KeyEvent.KEYCODE_BACK) { 69 if(webView.canGoBack()) {//当webview不是处于第一页面时,返回上一个页面 70 webView.goBack(); 71 return true; 72 } 73 else {//当webview处于第一页面时,直接退出程序 74 System.exit(0); 75 } 76 77 78 } 79 return super.onKeyDown(keyCode, event); 80 } 81
知识延伸:
一、Handler的定义:
主要接受子线程发送的数据, 并用此数据配合主线程更新UI。
解释:当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件, 进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。 如果此时需要一个耗时的操作,例如: 联网读取数据, 或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,,Android主线程是线程不安全的, 也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。 这个时候,Handler就出现了。,来解决这个复杂的问题 ,由于Handler运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。
二、Handler一些特点
handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用:
(1)安排消息或Runnable 在某个主线程中某个地方执行;
(2)安排一个动作在不同的线程中执行。
Handler中分发消息的一些方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
以上post类方法允许你排列一个Runnable对象到主线程队列中,
sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新。
三、Handler实例
子类需要继承Hendler类,并重写handleMessage(Message msg) 方法, 用于接受线程数据。
以下为一个实例,它实现的功能为:通过线程修改界面Button的内容
- public class MyHandlerActivity extends Activity {
- Button button;
- MyHandler myHandler;
- protected void onCreate(Bundle savedInstanceState) {
- super。onCreate(savedInstanceState);
- setContentView(R。layout。handlertest);
- button = (Button) findViewById(R。id。button);
- myHandler = new MyHandler();
- // 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据
- // Handler有两个作用, (1) : 定时执行Message和Runnalbe 对象
- // (2): 让一个动作,在不同的线程中执行。
- // 它安排消息,用以下方法
- // post(Runnable)
- // postAtTime(Runnable,long)
- // postDelayed(Runnable,long)
- // sendEmptyMessage(int)
- // sendMessage(Message);
- // sendMessageAtTime(Message,long)
- // sendMessageDelayed(Message,long)
- // 以上方法以 post开头的允许你处理Runnable对象
- //sendMessage()允许你处理Message对象(Message里可以包含数据,)
- MyThread m = new MyThread();
- new Thread(m)。start();
- }
- /**
- * 接受消息,处理消息 ,此Handler会与当前主线程一块运行
- * */
- class MyHandler extends Handler {
- public MyHandler() {
- }
- public MyHandler(Looper L) {
- super(L);
- }
- // 子类必须重写此方法,接受数据
- @Override
- public void handleMessage(Message msg) {
- // TODO Auto-generated method stub
- Log。d("MyHandler", "handleMessage。。。。。。");
- super。handleMessage(msg);
- // 此处可以更新UI
- Bundle b = msg。getData();
- String color = b。getString("color");
- MyHandlerActivity。this。button。append(color);
- }
- }
- class MyThread implements Runnable {
- public void run() {
- try {
- Thread。sleep(10000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e。printStackTrace();
- }
- Log。d("thread。。。。。。。", "mThread。。。。。。。。");
- Message msg = new Message();
- Bundle b = new Bundle();// 存放数据
- b。putString("color", "我的");
- msg。setData(b);
- MyHandlerActivity。this。myHandler。sendMessage(msg); // 向Handler发送消息,更新UI
- }
- }
- }
progressbar与handle结合的一个例子:
谷歌官方的一个实例:
实现动态更新ProgressBar的水平进度条:
布局文件progressBar.xml如下:
- <span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <!--大型环状进度条-->
- <ProgressBar
- android:id="@+id/bar1"
- style="@android:style/Widget.ProgressBar.Large"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- <!--中型环状进度条-->
- <ProgressBar
- android:id="@+id/bar2"
- style="@android:style/Widget.ProgressBar"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- <!--小型环状进度条-->
- <ProgressBar
- android:id="@+id/bar3"
- style="@android:style/Widget.ProgressBar.Small"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
- <TextView
- android:id="@+id/tv1"
- android:layout_width="match_parent"
- android:layout_height="25dp"/>
- <!--水平进度条-->
- <ProgressBar
- android:id="@+id/bar4"
- style="@android:style/Widget.ProgressBar.Horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout></span>
MyprogressBar.java代码如下:
- <span style="font-size:18px;">package com.example.lenovo.photobrowse;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.support.annotation.Nullable;
- import android.support.v7.app.AppCompatActivity;
- import android.widget.ProgressBar;
- import android.widget.TextView;
- import android.widget.Toast;
- import java.util.Random;
- /**
- * Created by lenovo on 2016/5/24.
- */
- public class MyprogessBar extends AppCompatActivity {
- //该程序模拟填充长度为100的数组
- private int[] data = new int[100];
- private int hasData = 0;
- //记录progressBar的完成程度
- private int status = 0;
- private ProgressBar bar1;
- private ProgressBar bar2;
- private TextView tv;
- //创建一个负责更新进度的hander
- Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- if(msg.what == 0x111){
- bar1.setProgress(status);
- bar2.setProgress(status);
- tv.setText("已完成"+status+"%");
- if(status==100)
- Toast.makeText(MyprogessBar.this,"以下载完成",Toast.LENGTH_SHORT).show();
- }
- }
- };
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.progresssbar_layout);
- bar1 = (ProgressBar) findViewById(R.id.bar1);
- bar2 = (ProgressBar) findViewById(R.id.bar4);
- tv = (TextView) findViewById(R.id.tv1);
- //启动线程来执行任务
- new Thread(){
- @Override
- public void run() {
- while(status<100) {
- //获取耗时操作的完成百分比
- status = doWork();
- //发送消息
- mHandler.sendEmptyMessage(status);
- }
- }
- }.start();
- }
- //模拟一个耗时的操作
- private int doWork() {
- data[hasData++] = (int)(Math.random()*100);
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return hasData;
- }
还有一个例子:
ProgressBar进度条,分为旋转进度条和水平进度条,进度条的样式根据需要自定义,之前一直不明白进度条如何在实际项目中使用,网上演示进度条的案例大多都是通过Button点击增加、减少进度值,使用方法incrementProgressBy(int),最简单的做法是在xml布局文件中放置ProgressBar空间,然后再MainActivity中触发事件后执行incrementProgressBy(int),代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = "cn.teachcourse.www.MainActivity" android:orientation = "vertical" android:gravity = "center" android:id = "@+id/onclick_ll" > <!--点击LinearLayout控件,改变ProgressBar进度--> < ProgressBar android:id = "@+id/progressBar_horizontal" style = "?android:attr/progressBarStyleHorizontal" android:layout_width = "match_parent" android:layout_height = "15dp" android:layout_marginTop = "28dp" /> </ LinearLayout > |
在Java代码中非常的简单,如下图:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public class MainActivity extends ActionBarActivity implements View.OnClickListener{ private ProgressBar pb_large; private ProgressBar pb_normal; private ProgressBar pb_small; private ProgressBar pb_horizontal; private int readed; private int progressValue; private LinearLayout onclick_ll; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); Drawable drawable = getResources().getDrawable( R.drawable.progress_bar_states); pb_horizontal.setProgressDrawable(drawable); //设置水平滚动条的样式 pb_horizontal.setMax( 100 ); //设置进度条的最大值 onclick_ll.setOnClickListener( this ); //LinearLayout添加监视器 } private void initView() { pb_horizontal = (ProgressBar) findViewById(R.id.progressBar_horizontal); onclick_ll=(LinearLayout)findViewById(R.id.onclick_ll); } @Override public void onClick(View v) { pb_horizontal.incrementProgressBy( 5 ); if (pb_horizontal.getProgress()==pb_horizontal.getMax()){ Toast.makeText( this , "进度条已经达到最大值,进度条消失" , Toast.LENGTH_LONG).show(); pb_horizontal.setProgress( 0 ); //当达到最大之后,进度值归0 } } } |
我们这里展示的是ProgressBar读取文件字节流后,展示的读取进度条,上面的简单演示就不说了。
效果图:ProgressBar读取文件字节流演示
这里展示文件读取字节流过程中,ProgressBar进度变化情况,当读取完成,进度条刚好满,这在我们实际项目开发中,经常需要使用到,如何成为我写这篇文章的原因。
布局文件progress_horizontal_read_data.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<LinearLayout 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" tools:context= "cn.teachcourse.www.MainActivity" android:orientation= "vertical" android:gravity= "center" > <TextView android:layout_width= "match_parent" android:layout_height= "wrap_content" android:layout_weight= "1" android:id= "@+id/read_data_tv" android:text= "@string/read_info" android:scrollbars= "vertical" android:layout_marginLeft= "10dp" android:layout_marginRight= "10dp" android:lineSpacingExtra= "10dp" /> <ProgressBar android:id= "@+id/progressBar_horizontal_read_data" style= "?android:attr/progressBarStyleHorizontal" android:layout_width= "match_parent" android:layout_height= "15dp" android:layout_marginTop= "28dp" /> </LinearLayout> |
Java代码ProgressBarActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
public class ProgressBarActivity extends ActionBarActivity { private ProgressBar pb_horizontal; private int readed; private int progressValue; private TextView mReadProgress_TV; private String mData; private StringBuffer sb; //每次读取到的字节数据存储 int length; //标记文件大小 private Thread thread; //读取文件线程 @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.progress_horizontal_read_data); initView(); Drawable drawable = getResources().getDrawable( R.drawable.progress_bar_states); String path = Environment.getExternalStorageDirectory() + "/ProgressMonitor.txt" ; // ProgressMonitor.txt是我存到sdcard中的一个文件,可以自定义文件名大小位置 final File file = new File(path); if (file.exists()){ length=( int ) file.length(); pb_horizontal.setMax(length); //设置进度条最大值 pb_horizontal.setProgressDrawable(drawable); //自定义进度条样式 } //启动一个线程读取文件内容 thread= new Thread() { @Override public void run() { readFromFile(file.getAbsolutePath()); } }; thread.start(); } Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 110 : progressValue += msg.arg1; pb_horizontal.setProgress(progressValue); mReadProgress_TV.setText(sb.toString()); if (progressValue==length){ Toast.makeText(ProgressBarActivity. this , "文件读取完成" , Toast.LENGTH_LONG).show(); } Log.d( "progressValue-------------->" , progressValue + "" ); break ; } } }; public void readFromFile(String path) { FileInputStream fis; try { fis = new FileInputStream(path); DataInputStream dis = new DataInputStream(fis); sb= new StringBuffer(); byte b[] = new byte [ 10 ]; // 每次读取1字节 while ((readed = dis.read(b)) != - 1 ) { Message msg = new Message(); msg.arg1 = readed; msg.what = 110 ; mData= new String(b, 0 ,readed); sb.append(mData); handler.sendMessage(msg); try { Thread.sleep( 1000 ); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } dis.close(); fis.close(); } catch (Exception e) { e.printStackTrace(); } } private void initView() { pb_horizontal = (ProgressBar) findViewById(R.id.progressBar_horizontal_read_data); mReadProgress_TV=(TextView)findViewById(R.id.read_data_tv); } } |
这里使用Handler消息处理,每次读取到数据后更新进度条,将读取的字节数据放置在arg1中,在Handler的handleMessage方法中设置进度值,同时刷新TextView显示的内容,这里难就难在如何获取每次读取的字节数,然后刷新进度条,在字节流的学习中,我们可以自定义每次读取字节大小,File类中可以获取到文件的总大小,最终我们得到上面图片中的效果。在这个例子里面,我们可以将读取手机卡的文件,换成下载文件,传输文件同样适应。