1344924203_8274  

   

        

1、java內置的觀察者模式:

由Java 中的Observable 類和 Observer 接口組成

(1) Observable 類代表 被觀察者 (java.util.Observable )

主要方法有:

void setChanged() : 設置被觀察者的狀態已經被改變
void addObserver(Observer observer) : 添加觀察者
int countObservers() : 返回所有觀察者的數目
void deleteObserver(Observer observer) :刪除指定觀察者
void deleteObservers() : 刪除所有觀察者
boolean hasChanged() : 被觀察者的狀態是否被改變,如果是則返回true,否則返回false
void notifyObservers() : 通知所有觀察者

void notifyObservers(Object arg) : 通知所有觀察者(参數一般設定为被改變的屬性)
void clearChanged() :清除被觀察者狀態的改變

(2) Observer 接口代表 觀察者 (java.util.Observer )

void update(Observable observable, Object arg) :當 被觀察者 調用 notifyObservers() 方法
時,會根據被觀察者的 hasChanged() 方法 來判斷 它的狀態是否被改變, 如果被觀察者的狀態被改變了,則
會調用 觀察者 的 update 方法,参數 observable 为 被觀察者對象, arg 为調用 notifyObservers( Object arg ) 時傳入的参數 arg ,如果調用的是 notifyObservers() 方法, 則 arg 为 null。

值得注意的是:在Observable裏,在notify()時必須先調用setChanged()方法,此方式表明狀態更新...

貼上代碼:

ImageZoomState類:

 

package hfut.gmm;

import java.util.Observable;

public class ImageZoomState extends Observable {
	private float mZoom = 1.0f;// 控制圖片縮放的變量,表示縮放倍數,值越大圖像越大
	private float mPanX = 0.5f;// 控制圖片水平方向移動的變量,值越大圖片可視區域的左邊界距離圖片左邊界越遠,圖像越靠左,值为0.5f時居中
	private float mPanY = 0.5f;// 控制圖片水平方向移動的變量,值越大圖片可視區域的上邊界距離圖片上邊界越遠,圖像越靠上,值为0.5f時居中

	public float getmZoom() {
		return mZoom;
	}

	public void setmZoom(float mZoom) {
		if (this.mZoom != mZoom) {
			this.mZoom = mZoom < 1.0f ? 1.0f : mZoom;// 保證圖片最小为原始狀態
			if (this.mZoom == 1.0f) {// 返回初始大小時,使其位置也恢复原始位置
				this.mPanX = 0.5f;
				this.mPanY = 0.5f;
			}
			this.setChanged();
		}
	}

	public float getmPanX() {
		return mPanX;
	}

	public void setmPanX(float mPanX) {
		if (mZoom == 1.0f) {// 使圖为原始大小時不能移動
			return;
		}
		if (this.mPanX != mPanX) {
			this.mPanX = mPanX;
			this.setChanged();
		}
	}

	public float getmPanY() {
		return mPanY;
	}

	public void setmPanY(float mPanY) {
		if (mZoom == 1.0f) {// 使圖为原始大小時不能移動
			return;
		}
		if (this.mPanY != mPanY) {
			this.mPanY = mPanY;
			this.setChanged();
		}
	}

	public float getZoomX(float aspectQuotient) {
		return Math.min(mZoom, mZoom * aspectQuotient);
	}

	public float getZoomY(float aspectQuotient) {
		return Math.min(mZoom, mZoom / aspectQuotient);
	}
}


SimpleImageZoomListener類:

 

 

package hfut.gmm;

import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class SimpleImageZoomListener implements View.OnTouchListener {
	private ImageZoomState mState;// 圖片縮放和移動狀態
	private final float SENSIBILITY = 0.8f;// 圖片移動時的靈敏度

	/**
	 * 變化的起始點坐標
	 */
	private float sX;
	private float sY;
	/**
	 * 不變的起始點坐標,用於判斷手指是否進行了移動,從而在UP事件中判斷是否为點擊事件
	 */
	private float sX01;
	private float sY01;
	/**
	 * 兩觸摸點間的最初距離
	 */
	private float sDistance;

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		int action = event.getAction();
		int pointNum = event.getPointerCount();// 獲取觸摸點數
		if (pointNum == 1) {// 單點觸摸,用來實現圖像的移動和相應點擊事件
			Log.d("Infor", "一個觸點");
			float mX = event.getX();// 記錄不斷移動的觸摸點x坐標
			float mY = event.getY();// 記錄不斷移動的觸摸點y坐標
			switch (action) {
			case MotionEvent.ACTION_DOWN:
				// 記錄起始點坐標
				sX01 = mX;
				sY01 = mY;
				sX = mX;
				sY = mY;
				return false;// 必須return false 否則不響應點擊事件
			case MotionEvent.ACTION_MOVE:
				float dX = (mX - sX) / v.getWidth();
				float dY = (mY - sY) / v.getHeight();
				mState.setmPanX(mState.getmPanX() - dX * SENSIBILITY);
				mState.setmPanY(mState.getmPanY() - dY * SENSIBILITY);
				mState.notifyObservers();
				// 更新起始點坐標
				sX = mX;
				sY = mY;
				break;
			case MotionEvent.ACTION_UP:
				if (event.getX() == sX01 && event.getY() == sY01) {
					return false;// return false 執行點擊事件
				}
				break;
			}
		}

		if (pointNum == 2) {// 多點觸摸,用來實現圖像的縮放
			// 記錄不斷移動的一個觸摸點坐標
			Log.d("Infor", "二個觸點");
			float mX0 = event.getX(event.getPointerId(0));
			float mY0 = event.getY(event.getPointerId(0));
			// 記錄不斷移動的令一個觸摸點坐標
			float mX1 = event.getX(event.getPointerId(1));
			float mY1 = event.getY(event.getPointerId(1));

			float distance = this.getDistance(mX0, mY0, mX1, mY1);
			switch (action) {
			case MotionEvent.ACTION_POINTER_2_DOWN:
			case MotionEvent.ACTION_POINTER_1_DOWN:
				sDistance = distance;
				break;
			case MotionEvent.ACTION_POINTER_1_UP:
				// 注意:松開第一個觸摸點後的手指滑動就變成了以第二個觸摸點为起始點的移動,所以要以第二個觸摸點坐標值为起始點坐標賦值
				sX = mX1;
				sY = mY1;
				break;
			case MotionEvent.ACTION_POINTER_2_UP:
				// 注意:松開第二個觸摸點後的手指滑動就變成了以第二個觸摸點为起始點的移動,所以要以第一個觸摸點坐標值为起始點坐標賦值
				sX = mX0;
				sY = mY0;
				break;
			case MotionEvent.ACTION_MOVE:
				// float dDistance = (distance - sDistance) / sDistance;
				// mState.setmZoom(mState.getmZoom()
				// * (float) Math.pow(5, dDistance));
				mState.setmZoom(mState.getmZoom() * distance / sDistance);
				mState.notifyObservers();
				sDistance = distance;
				break;
			}
		}
		return true;// 必須返回true,具體請查詢Android事件攔截機制的相關資料
	}

	/**
	 * //返回( mX0, mY0)與(( mX1, mY1)兩點間的距離
	 * 
	 * @param mX0
	 * @param mX1
	 * @param mY0
	 * @param mY1
	 * @return
	 */
	private float getDistance(float mX0, float mY0, float mX1, float mY1) {
		double dX2 = Math.pow(mX0 - mX1, 2);// 兩點橫坐標差的平法
		double dY2 = Math.pow(mY0 - mY1, 2);// 兩點縱坐標差的平法
		return (float) Math.pow(dX2 + dY2, 0.5);
	}

	public void setZoomState(ImageZoomState state) {
		mState = state;
	}
}


ImageZoomView類:

 

 

package hfut.gmm;

import java.util.Observable;
import java.util.Observer;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;

public class ImageZoomView extends View implements Observer {
	private Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
	private Rect mRectSrc = new Rect();
	private Rect mRectDst = new Rect();
	private float mAspectQuotient;

	private Bitmap mBitmap;
	private ImageZoomState mZoomState;

	public ImageZoomView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	@Override
	public void update(Observable observable, Object data) {
		this.invalidate();
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		if (mBitmap != null && mZoomState != null) {
			int viewWidth = this.getWidth();
			int viewHeight = this.getHeight();
			int bitmapWidth = mBitmap.getWidth();
			int bitmapHeight = mBitmap.getHeight();

			float panX = mZoomState.getmPanX();
			float panY = mZoomState.getmPanY();
			float zoomX = mZoomState.getZoomX(mAspectQuotient) * viewWidth
					/ bitmapWidth;// 相當於viewHeight/bitmapHeight*mZoom
			float zoomY = mZoomState.getZoomY(mAspectQuotient) * viewHeight
					/ bitmapHeight;// 相當於viewWidth/bitmapWidth*mZoom

			// Setup source and destination rectangles
			// 這裏假定圖片的高和寬都大於顯示區域的高和寬,如果不是在下面做調整
			mRectSrc.left = (int) (panX * bitmapWidth - viewWidth / (zoomX * 2));
			mRectSrc.top = (int) (panY * bitmapHeight - viewHeight
					/ (zoomY * 2));
			mRectSrc.right = (int) (mRectSrc.left + viewWidth / zoomX);
			mRectSrc.bottom = (int) (mRectSrc.top + viewHeight / zoomY);

			mRectDst.left = this.getLeft();
			mRectDst.top = this.getTop();
			mRectDst.right = this.getRight();
			mRectDst.bottom = this.getBottom();

			// Adjust source rectangle so that it fits within the source image.
			// 如果圖片寬或高小於顯示區域寬或高(組件大小)或者由於移動或縮放引起的下面條件成立則調整矩形區域邊界
			if (mRectSrc.left < 0) {
				mRectDst.left += -mRectSrc.left * zoomX;
				mRectSrc.left = 0;
			}
			if (mRectSrc.right > bitmapWidth) {
				mRectDst.right -= (mRectSrc.right - bitmapWidth) * zoomX;
				mRectSrc.right = bitmapWidth;
			}

			if (mRectSrc.top < 0) {
				mRectDst.top += -mRectSrc.top * zoomY;
				mRectSrc.top = 0;
			}
			if (mRectSrc.bottom > bitmapHeight) {
				mRectDst.bottom -= (mRectSrc.bottom - bitmapHeight) * zoomY;
				mRectSrc.bottom = bitmapHeight;
			}

			// 把bitmap的一部分(就是src所包括的部分)繪制到顯示區中dst指定的矩形處.關鍵就是dst,它確定了bitmap要畫的大小跟位置
			// 注:兩個矩形中的坐標位置是相對於各自本身的而不是相對於屏幕的。
			canvas.drawBitmap(mBitmap, mRectSrc, mRectDst, mPaint);
		}
	}

	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		// TODO Auto-generated method stub
		super.onLayout(changed, left, top, right, bottom);
		this.calculateAspectQuotient();
	}

	public void setImageZoomState(ImageZoomState zoomState) {
		if (mZoomState != null) {
			mZoomState.deleteObserver(this);
		}
		mZoomState = zoomState;
		mZoomState.addObserver(this);
		invalidate();
	}

	public void setImage(Bitmap bitmap) {
		mBitmap = bitmap;
		this.calculateAspectQuotient();
		invalidate();

	}

	private void calculateAspectQuotient() {
		if (mBitmap != null) {
			mAspectQuotient = (float) (((float) mBitmap.getWidth() / mBitmap
					.getHeight()) / ((float) this.getWidth() / this.getHeight()));
		}
	}
}


入口MainActivity類:

 

 

package hfut.gmm;

import android.app.Activity;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.ZoomControls;

public class MainActivity extends Activity {
    /** Called when the activity is first created. */
	private ZoomControls zoomCtrl;// 系統自帶的縮放控制組件
	private ImageZoomView zoomView;// 自定義的圖片顯示組件
	private ImageZoomState zoomState;// 圖片縮放和移動狀態類
	private SimpleImageZoomListener zoomListener;// 縮放事件監聽器
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.main);
        zoomState = new ImageZoomState();

		zoomListener = new SimpleImageZoomListener();
		zoomListener.setZoomState(zoomState);

		zoomCtrl = (ZoomControls) findViewById(R.id.zoomCtrl);
		this.setImageController();

	   zoomView = (ImageZoomView) findViewById(R.id.zoomView);
	 //	zoomView.setImage(bitmap);
	    zoomView.setImage(BitmapFactory.decodeResource(this.getResources(), R.drawable.index));
		zoomView.setImageZoomState(zoomState);
		zoomView.setOnTouchListener(zoomListener);
		zoomView.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				setFullScreen();
			}
		});

	}

	private void setImageController() {
		zoomCtrl.setOnZoomInClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				float z = zoomState.getmZoom() + 0.25f;
				zoomState.setmZoom(z);
				zoomState.notifyObservers();
			}
		});
		zoomCtrl.setOnZoomOutClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				float z = zoomState.getmZoom() - 0.25f;// 圖像大小減少原來的0.25倍
				zoomState.setmZoom(z);
				zoomState.notifyObservers();
			}
		});
	}

	/**
	 * 隱藏處ImageZoomView外地其他組件,全屏顯示
	 */
	private void setFullScreen() {
		if (zoomCtrl != null) {
			if (zoomCtrl.getVisibility() == View.VISIBLE) {
				// zoomCtrl.setVisibility(View.GONE);
				zoomCtrl.hide(); // 有過度效果

			} else if (zoomCtrl.getVisibility() == View.GONE) {
				// zoomCtrl.setVisibility(View.VISIBLE);
				zoomCtrl.show();// 有過渡效果

			}
		} 

}
}

 

XML文件:

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

    <hfut.gmm.ImageZoomView
        android:id="@+id/zoomView"
        android:layout_width="fill_parent"
        
        android:layout_height="fill_parent" >
    </hfut.gmm.ImageZoomView>

    <ZoomControls
        android:id="@+id/zoomCtrl"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
       
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true" >
    </ZoomControls>

</RelativeLayout>


From:CSDN
創作者介紹
創作者 shadow 的頭像
shadow

資訊園

shadow 發表在 痞客邦 留言(0) 人氣()