1. 程式人生 > >Android webView和js互動

Android webView和js互動

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <WebView
        android:id="@+id/webview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="9" />

</LinearLayout>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <!-- TODO 預設佔位圖 -->

    <wst.webview.ZoomableImageView
        android:id="@+id/show_webimage_imageview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="matrix"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/show_webimage_imagepath_textview"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="#ffff0000" />

</LinearLayout>

package wst.webview;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

@SuppressLint({ "SetJavaScriptEnabled", "JavascriptInterface" })
public class MainActivity extends Activity {

	private WebView contentWebView = null;

	@SuppressLint("SetJavaScriptEnabled")
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		contentWebView = (WebView) findViewById(R.id.webview);
		// 啟用javascript
		contentWebView.getSettings().setJavaScriptEnabled(true);
		// 隨便找了個帶圖片的網站
		contentWebView.loadUrl("http://www.sina.com.cn/");
		// 新增js互動介面類,並起別名 imagelistner
		contentWebView.addJavascriptInterface(new scriptInterface(this), "imagelistner");
		contentWebView.setWebViewClient(new MyWebViewClient());

	}

	// 注入js函式監聽
	@android.webkit.JavascriptInterface
	private void addImageClickListner() {
		// 這段js函式的功能就是,遍歷所有的img幾點,並新增onclick函式,在還是執行的時候呼叫本地介面傳遞url過去
	     
		contentWebView.loadUrl("javascript:(function(){" +
		"var objs = document.getElementsByTagName(\"img\"); " + 
				"for(var i=0;i<objs.length;i++)  " + 
		"{"
				+ "    objs[i].onclick=function()  " + 
		"    {  " 
				+ "        window.imagelistner.openImage(this.src);  " + 
		"    }  " + 
		"}" + 
		"})()");
	}

	// js通訊介面
	 class scriptInterface {

		private Context context;
		public scriptInterface(Context context) {
			this.context = context;
		}
		@android.webkit.JavascriptInterface
		public void openImage(String img) {
			System.out.println(img);
			//
			Intent intent = new Intent();
			intent.putExtra("image", img);
			intent.setClass(context, ShowWebImageActivity.class);
			context.startActivity(intent);
			System.out.println(img);
		}
	}

	// 監聽
	private class MyWebViewClient extends WebViewClient {
		@Override
		public boolean shouldOverrideUrlLoading(WebView view, String url) {

			return super.shouldOverrideUrlLoading(view, url);
		}

		@Override
		public void onPageFinished(WebView view, String url) {

			view.getSettings().setJavaScriptEnabled(true);

			super.onPageFinished(view, url);
			// html載入完成之後,新增監聽圖片的點選js函式
			addImageClickListner();

		}

		@Override
		public void onPageStarted(WebView view, String url, Bitmap favicon) {
			view.getSettings().setJavaScriptEnabled(true);

			super.onPageStarted(view, url, favicon);
		}

		@Override
		public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {

			super.onReceivedError(view, errorCode, description, failingUrl);

		}
	}

}

package wst.webview;



public interface OnImageTouchedListener {

	void onImageTouched();

}

package wst.webview;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.TextView;

public class ShowWebImageActivity extends Activity {
	private TextView imageTextView = null;
	private String imagePath = null;
	private ZoomableImageView imageView = null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.show_webimage);
		this.imagePath = getIntent().getStringExtra("image");

		this.imageTextView = (TextView) findViewById(R.id.show_webimage_imagepath_textview);
		imageTextView.setText(this.imagePath);
		imageView = (ZoomableImageView) findViewById(R.id.show_webimage_imageview);

//		try {
//			imageView.setImageBitmap(((BitmapDrawable) ShowWebImageActivity.loadImageFromUrl(this.imagePath)).getBitmap());
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
		LoadImageAsyncTask task = new LoadImageAsyncTask();
		task.setImageView(imageView); 
		task.execute(this.imagePath); // 執行任務,引數與 doInBackground(String... params) 方法引數一致
	}

	public static Drawable loadImageFromUrl(String url) throws IOException {

		URL m = new URL(url);
		InputStream i = (InputStream) m.getContent();
		Drawable d = Drawable.createFromStream(i, "src");
		return d;
	}
	
	
	
	    
	    
	    
	    /**
	     * 非同步載入圖片
	     * 

	     */
	    public class LoadImageAsyncTask extends AsyncTask<String, Integer, Bitmap> {
	     private ZoomableImageView imageView;

	     @Override
	     protected void onPostExecute(Bitmap bitmap) {
	      if (null != imageView) {
	       imageView.setImageBitmap(bitmap);
	      }
	     }
	     
	     // 設定圖片檢視例項
	     public void setImageView(ZoomableImageView image) {
	      this.imageView = image;
	     }

	     @Override
	     protected Bitmap doInBackground(String... params) {
	      Bitmap bitmap = GetBitmapByUrl(params[0]); // 呼叫前面 ApacheUtility 類的方法

	      return bitmap;
	     }
	    }
	    
	    
	    /**
	     * 獲取圖片流
	     * 
	     * @param uri 圖片地址

	     * @return
	     * @throws MalformedURLException
	     */
	    public static InputStream GetImageByUrl(String uri) throws MalformedURLException {
	     URL url = new URL(uri);
	     URLConnection conn;
	     InputStream is;
	     try {
	      conn = url.openConnection();
	      conn.connect();
	      is = conn.getInputStream();

	      // 或者用如下方法

	      // is=(InputStream)url.getContent();
	      return is;
	     } catch (IOException e) {
	      e.printStackTrace();
	     }

	     return null;
	    }

	    

	    

	   /**
	     * 獲取Bitmap

	     * 
	     * @param uri 圖片地址
	     * @return
	     */
	    public static Bitmap GetBitmapByUrl(String uri) {

	     Bitmap bitmap;  
	     InputStream is;
	     try {

	      is =  GetImageByUrl(uri); 

	      bitmap = BitmapFactory.decodeStream(is);
	      is.close();

	      return bitmap;

	     } catch (MalformedURLException e) {
	      e.printStackTrace();

	     } catch (IOException e) {
	      e.printStackTrace();
	     }

	     return null;
	    }

	    

	    

	   /**
	     * 獲取Drawable

	     * 
	     * @param uri  圖片地址

	     * @return
	     */
	    public static Drawable GetDrawableByUrl(String uri) {

	     Drawable drawable;  
	     InputStream is;
	     try {

	      is =  GetImageByUrl(uri); 

	      drawable= Drawable.createFromStream(is, "src");

	      is.close();

	      return drawable;

	     } catch (MalformedURLException e) {
	      e.printStackTrace();

	     } catch (IOException e) {
	      e.printStackTrace();
	     }

	     return null;
	    }
	
	
	
	
	
	
	
	
	
	
	
	
	
	
}
package wst.webview;



import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Build;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;

@SuppressLint("NewApi")
public class ZoomableImageView extends View {

	// Statics
	static final float sPanRate = 7;
	static final float sScaleRate = 1.25F;
	static final int sPaintDelay = 250;
	static final int sAnimationDelay = 500;

	// This is the base transformation which is used to show the image
	// initially.  The current computation for this shows the image in
	// it's entirety, letterboxing as needed.  One could chose to
	// show the image as cropped instead.  
	//
	// This matrix is recomputed when we go from the thumbnail image to
	// the full size image.
	private Matrix mBaseMatrix = new Matrix();

	// This is the supplementary transformation which reflects what 
	// the user has done in terms of zooming and panning.
	//
	// This matrix remains the same when we go from the thumbnail image
	// to the full size image.
	private Matrix mSuppMatrix = new Matrix();

	// This is the final matrix which is computed as the concatentation
	// of the base matrix and the supplementary matrix.
	private Matrix mDisplayMatrix = new Matrix();

	// A replacement ImageView matrix
	private Matrix mMatrix = new Matrix();

	// Used to filter the bitmaps when hardware acceleration is not enabled
	private Paint mPaint;

	// Temporary buffer used for getting the values out of a matrix.
	private float[] mMatrixValues = new float[9];

	// The current bitmap being displayed.
	private Bitmap mBitmap;

	// Dimensions for the view
	private int mThisWidth = -1, mThisHeight = -1;

	// The max zoom for the view, determined programatically
	private float mMaxZoom;

	// If not null, calls setImageBitmap when onLayout is triggered
	private Runnable mOnLayoutRunnable = null;

	// Stacked to the internal queue to invalidate the view
	private Runnable mRefresh = null;

	// Stacked to the internal queue to scroll the view
	private Runnable mFling = null;

	// The time of the last draw operation
	private double mLastDraw = 0;

	// Scale and gesture listeners for the view
	private ScaleGestureDetector mScaleDetector;
	private GestureDetector mGestureDetector;
	
	// Single tap listener
	private OnImageTouchedListener mImageTouchedListener;

	// Programatic entry point
	public ZoomableImageView(Context context) {
		super(context);
		init( context );
	}
	
	// Set the single tap listener
	public void setOnImageTouchedListener( OnImageTouchedListener listener ){
		this.mImageTouchedListener = listener;
	}

	// XML entry point
	public ZoomableImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init( context );
	}

	// Setup the view
	private void init( Context context) {
		mPaint = new Paint();
		mPaint.setDither(true);
		mPaint.setFilterBitmap(true);
		mPaint.setAntiAlias(true);

		// Setup the refresh runnable
		mRefresh = new Runnable() {
			@Override
			public void run() {
				postInvalidate();
			}
		};

		// Setup the gesture and scale listeners
		mScaleDetector = new ScaleGestureDetector( context, new ScaleListener() );
		mGestureDetector = new GestureDetector(context, new MyGestureListener());
		
		// Force hardware acceleration
		if( Build.VERSION.SDK_INT >=  Build.VERSION_CODES.HONEYCOMB )
			setLayerType(View.LAYER_TYPE_HARDWARE, null);
	}

	// Get the bitmap for the view
	public Bitmap getImageBitmap(){
		return mBitmap;
	}

	// Free the bitmaps and matrices
	public void clear(){
		if(mBitmap!=null)
			mBitmap = null;
	}

	// When the layout is calculated, set the 
	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		mThisWidth = right - left;
		mThisHeight = bottom - top;
		Runnable r = mOnLayoutRunnable;
		if (r != null) {
			mOnLayoutRunnable = null;
			r.run();
		}
		if (mBitmap != null) {
			setBaseMatrix(mBitmap, mBaseMatrix);
			setImageMatrix(getImageViewMatrix());
		}
	}

	// Translate a given point through a given matrix.
	static private void translatePoint(Matrix matrix, float [] xy) {
		matrix.mapPoints(xy);
	}

	// Identical to the setImageMatrix method in ImageView
	public void setImageMatrix(Matrix m){
		if (m != null && m.isIdentity()) {
			m = null;
		}

		// don't invalidate unless we're actually changing our matrix
		if (m == null && !this.mMatrix.isIdentity() || m != null && !this.mMatrix.equals(m)) {
			this.mMatrix.set(m);
			invalidate();
		}
	}

	// Sets the bitmap for the image and resets the base
	public void setImageBitmap(final Bitmap bitmap) {
		final int viewWidth = getWidth();
		
		if( Build.VERSION.SDK_INT >=  Build.VERSION_CODES.HONEYCOMB && bitmap!=null && bitmap.getHeight()>1800 )
			setLayerType(View.LAYER_TYPE_SOFTWARE, null);

		if (viewWidth <= 0)  {
			mOnLayoutRunnable = new Runnable() {
				public void run() {
					setImageBitmap(bitmap);
				}
			};
			return;
		}

		if (bitmap != null) {
			setBaseMatrix(bitmap, mBaseMatrix);
			this.mBitmap = bitmap;
		} else {
			mBaseMatrix.reset();
			this.mBitmap = bitmap;
		}

		mSuppMatrix.reset();
		setImageMatrix(getImageViewMatrix());
		mMaxZoom = maxZoom();
		
		// Set the image to fit the screen
		zoomTo(zoomDefault());
	}

	// Unchanged from ImageViewTouchBase
	// Center as much as possible in one or both axis.  Centering is
	// defined as follows:  if the image is scaled down below the 
	// view's dimensions then center it (literally).  If the image
	// is scaled larger than the view and is translated out of view
	// then translate it back into view (i.e. eliminate black bars).
	protected void center(boolean vertical, boolean horizontal, boolean animate) {
		if (mBitmap == null)
			return;

		Matrix m = getImageViewMatrix();

		float [] topLeft  = new float[] { 0, 0 };
		float [] botRight = new float[] { mBitmap.getWidth(), mBitmap.getHeight() };

		translatePoint(m, topLeft);
		translatePoint(m, botRight);

		float height = botRight[1] - topLeft[1];
		float width  = botRight[0] - topLeft[0];

		float deltaX = 0, deltaY = 0;

		if (vertical) {
			int viewHeight = getHeight();
			if (height < viewHeight) {
				deltaY = (viewHeight - height)/2 - topLeft[1];
			} else if (topLeft[1] > 0) {
				deltaY = -topLeft[1];
			} else if (botRight[1] < viewHeight) {
				deltaY = getHeight() - botRight[1];
			}
		}

		if (horizontal) {
			int viewWidth = getWidth();
			if (width < viewWidth) {
				deltaX = (viewWidth - width)/2 - topLeft[0];
			} else if (topLeft[0] > 0) {
				deltaX = -topLeft[0];
			} else if (botRight[0] < viewWidth) {
				deltaX = viewWidth - botRight[0];
			}
		}

		postTranslate(deltaX, deltaY);
		if (animate) {
			Animation a = new TranslateAnimation(-deltaX, 0, -deltaY, 0);
			a.setStartTime(SystemClock.elapsedRealtime());
			a.setDuration(250);
			setAnimation(a);
		}
		setImageMatrix(getImageViewMatrix());
	}

	// Unchanged from ImageViewTouchBase
	protected float getValue(Matrix matrix, int whichValue) {
		matrix.getValues(mMatrixValues);
		return mMatrixValues[whichValue];
	}

	// Get the scale factor out of the matrix.
	protected float getScale(Matrix matrix) {

		// If the bitmap is set return the scale
		if(mBitmap!=null)
			return getValue(matrix, Matrix.MSCALE_X);
		// Otherwise return the default value of 1
		else
			return 1f;
	}

	// Returns the current scale of the view 
	public float getScale() {
		return getScale(mSuppMatrix);
	}

	// Setup the base matrix so that the image is centered and scaled properly.
	private void setBaseMatrix(Bitmap bitmap, Matrix matrix) {
		float viewWidth = getWidth();
		float viewHeight = getHeight();

		matrix.reset();
		float widthScale = Math.min(viewWidth / (float)bitmap.getWidth(), 1.0f);
		float heightScale = Math.min(viewHeight / (float)bitmap.getHeight(), 1.0f);
		float scale;
		if (widthScale > heightScale) {
			scale = heightScale;
		} else {
			scale = widthScale;
		}
		matrix.setScale(scale, scale);
		matrix.postTranslate(
				(viewWidth  - ((float)bitmap.getWidth()  * scale))/2F, 
				(viewHeight - ((float)bitmap.getHeight() * scale))/2F);
	}


	// Combine the base matrix and the supp matrix to make the final matrix.
	protected Matrix getImageViewMatrix() {
		mDisplayMatrix.set(mBaseMatrix);
		mDisplayMatrix.postConcat(mSuppMatrix);
		return mDisplayMatrix;
	}

	// Sets the maximum zoom, which is a scale relative to the base matrix. It is calculated to show
	// the image at 400% zoom regardless of screen or image orientation. If in the future we decode
	// the full 3 megapixel image, rather than the current 1024x768, this should be changed down to
	// 200%.
	protected float maxZoom() {
		if (mBitmap == null)
			return 1F;

		float fw = (float) mBitmap.getWidth()  / (float)mThisWidth;
		float fh = (float) mBitmap.getHeight() / (float)mThisHeight;
		float max = Math.max(fw, fh) * 16;
		return max;
	}
	
	// Tries to make best use of the space by zooming the picture
	public float zoomDefault() {
		if (mBitmap == null)
			return 1F;

		float fw = (float)mThisWidth/(float)mBitmap.getWidth();
		float fh = (float)mThisHeight/(float)mBitmap.getHeight();
		return Math.max(Math.min(fw, fh),1);
	}

	// Unchanged from ImageViewTouchBase
	protected void zoomTo(float scale, float centerX, float centerY) {
		if (scale > mMaxZoom) {
			scale = mMaxZoom;
		}

		float oldScale = getScale();
		float deltaScale = scale / oldScale;

		mSuppMatrix.postScale(deltaScale, deltaScale, centerX, centerY);
		setImageMatrix(getImageViewMatrix());
		center(true, true, false);
	}

	// Unchanged from ImageViewTouchBase
	protected void zoomTo(final float scale, final float centerX, final float centerY, final float durationMs) {
		final float incrementPerMs = (scale - getScale()) / durationMs;
		final float oldScale = getScale();
		final long startTime = System.currentTimeMillis();

		// Setup the zoom runnable
		post(new Runnable() {
			public void run() {
				long now = System.currentTimeMillis();
				float currentMs = Math.min(durationMs, (float)(now - startTime));
				float target = oldScale + (incrementPerMs * currentMs);
				zoomTo(target, centerX, centerY);

				if (currentMs < durationMs) {
					post(this);
				}
			}
		});
	}

	// Unchanged from ImageViewTouchBase
	public void zoomTo(float scale) {
		float width = getWidth();
		float height = getHeight();

		zoomTo(scale, width/2F, height/2F);
	}

	// Unchanged from ImageViewTouchBase
	protected void zoomIn() {
		zoomIn(sScaleRate);
	}

	// Unchanged from ImageViewTouchBase
	protected void zoomOut() {
		zoomOut(sScaleRate);
	}

	// Unchanged from ImageViewTouchBase
	protected void zoomIn(float rate) {
		if (getScale() >= mMaxZoom) {
			return;     // Don't let the user zoom into the molecular level.
		}
		if (mBitmap == null) {
			return;
		}

		float width = getWidth();
		float height = getHeight();

		mSuppMatrix.postScale(rate, rate, width/2F, height/2F);
		setImageMatrix(getImageViewMatrix());

	}

	// Unchanged from ImageViewTouchBase
	protected void zoomOut(float rate) {
		if (mBitmap == null) {
			return;
		}

		float width = getWidth();
		float height = getHeight();

		Matrix tmp = new Matrix(mSuppMatrix);
		tmp.postScale(1F/sScaleRate, 1F/sScaleRate, width/2F, height/2F);
		if (getScale(tmp) < 1F) {
			mSuppMatrix.setScale(1F, 1F, width/2F, height/2F);
		} else {
			mSuppMatrix.postScale(1F/rate, 1F/rate, width/2F, height/2F);
		}
		setImageMatrix(getImageViewMatrix());
		center(true, true, false);

	}

	// Unchanged from ImageViewTouchBase
	protected void postTranslate(float dx, float dy) {
		mSuppMatrix.postTranslate(dx, dy);
	}

	// Fling a view by a distance over time
	protected void scrollBy( float distanceX, float distanceY, final float durationMs ){
		final float dx = distanceX;
		final float dy = distanceY;
		final long startTime = System.currentTimeMillis();

		mFling = new Runnable() {
			float old_x	= 0;
			float old_y	= 0;

			public void run()
			{
				long now = System.currentTimeMillis();
				float currentMs = Math.min( durationMs, now - startTime );
				float x = easeOut( currentMs, 0, dx, durationMs );
				float y = easeOut( currentMs, 0, dy, durationMs );
				postTranslate( ( x - old_x ), ( y - old_y ) );
				center(true, true, false);

				old_x = x;
				old_y = y;
				if ( currentMs < durationMs ) {
					post( this );
				}
			}
		};
		post( mFling );
	}

	// Gradually slows down a fling velocity
	private float easeOut( float time, float start, float end, float duration){
		return end * ( ( time = time / duration - 1 ) * time * time + 1 ) + start;
	}

	// Custom draw operation to draw the bitmap using mMatrix
	@Override
	protected void onDraw(Canvas canvas) {

		// Check if the bitmap was ever set
		if(mBitmap!=null && !mBitmap.isRecycled() ){

			// If the current version is above Gingerbread and the layer type is 
			// hardware accelerated, the paint is no longer needed
			if( Build.VERSION.SDK_INT >=  Build.VERSION_CODES.HONEYCOMB 
					&& getLayerType() == View.LAYER_TYPE_HARDWARE ){
				canvas.drawBitmap(mBitmap, mMatrix, null);
			}else{
				// Check if the time between draws has been met and draw the bitmap
				if( (System.currentTimeMillis()-mLastDraw) > sPaintDelay ){
					canvas.drawBitmap(mBitmap, mMatrix, mPaint);
					mLastDraw = System.currentTimeMillis();
				}

				// Otherwise draw the bitmap without the paint and resubmit a new request
				else{
					canvas.drawBitmap(mBitmap, mMatrix, null);
					removeCallbacks(mRefresh);
					postDelayed(mRefresh, sPaintDelay);
				}
			}
		}
	}

	// Adjusts the zoom of the view
	class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

		@Override
		public boolean onScale( ScaleGestureDetector detector )
		{	
			// Check if the detector is in progress in order to proceed
			if(detector!=null && detector.isInProgress() ){
				try{
					// Grab the scale
					float targetScale = getScale() * detector.getScaleFactor();
					// Correct for the min scale
					targetScale = Math.min( maxZoom(), Math.max( targetScale, 1.0f) );

					// Zoom and invalidate the view
					zoomTo( targetScale, detector.getFocusX(), detector.getFocusY() );
					invalidate();

					return true;
				}catch(IllegalArgumentException e){
					e.printStackTrace();
				}
			}
			return false;
		}
	}

	// Handles taps and scrolls of the view
	private class MyGestureListener extends
	GestureDetector.SimpleOnGestureListener {
		
		@Override
		public boolean onSingleTapConfirmed(MotionEvent e) {
			if(mImageTouchedListener!=null){
				mImageTouchedListener.onImageTouched();
				return false;
			}
			
			return super.onSingleTapConfirmed(e);
		}

		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

			// Skip if there are multiple points of contact
			if ( (e1!=null&&e1.getPointerCount() > 1) || (e2!=null&&e2.getPointerCount() > 1) || (mScaleDetector!=null && mScaleDetector.isInProgress()) ) 
				return false;

			// Scroll the bitmap
			if ( getScale() > zoomDefault() ) {
				removeCallbacks(mFling);
				postTranslate(-distanceX, -distanceY);
				center(true, true, false);
			}

			// Default case
			return true;
		}

		@Override
		public boolean onDoubleTap(MotionEvent e) {
			// If the zoom is over 1x, reset to 1x
			if ( getScale() > zoomDefault() ){
				zoomTo(zoomDefault());
			}
			// If the zoom is default, zoom into 2x
			else 
				zoomTo(zoomDefault()*3, e.getX(), e.getY(),200);

			// Always true as double tap was performed
			return true;
		}

		@Override
		public boolean onFling( MotionEvent e1, MotionEvent e2, float velocityX, float velocityY )
		{
			if ( (e1!=null&&e1.getPointerCount() > 1) || (e2!=null&&e2.getPointerCount() > 1) ) return false;
			if ( mScaleDetector.isInProgress() ) return false;

			try{
				float diffX = e2.getX() - e1.getX();
				float diffY = e2.getY() - e1.getY();

				if ( Math.abs( velocityX ) > 800 || Math.abs( velocityY ) > 800 ) {
					scrollBy( diffX / 2, diffY / 2, 300 );
					invalidate();
				}
			}catch(NullPointerException  e){

			}

			return super.onFling( e1, e2, velocityX, velocityY );
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {

		// If the bitmap was set, check the scale and gesture detectors
		if(mBitmap!=null){

			// Check the scale detector
			mScaleDetector.onTouchEvent( event );

			// Check the gesture detector
			if(!mScaleDetector.isInProgress())
				mGestureDetector.onTouchEvent( event );
		}

		// Default case
		return true;
	}
}

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="wst.webview"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="wst.webview.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
  
        <activity
            android:name="wst.webview.ShowWebImageActivity"
            android:configChanges="orientation"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:theme="@style/Transparent" >
        </activity>
    </application>

    <uses-permission android:name="android.permission.INTERNET" />
</manifest>