編輯:關於Android編程
首先推薦一下鴻洋大大的打造個性的圖片預覽與多點觸控視頻教程,這套教程教我們一步一步實現了多點觸控實現對圖片的平移和縮放的功能,這篇文章我將在鴻洋大大的基礎之上做了一些擴展功能:
1.圖片的慣性滑動
2.圖片縮放小於正常比例時,松手會自動回彈成正常比例
3.圖片縮放大於最大比例時,松手會自動回彈成最大比例
實現圖片的縮放,平移,雙擊縮放等基本功能的代碼如下,每一行代碼我都做了詳細的注釋<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
public class ZoomImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener,
View.OnTouchListener , ViewTreeObserver.OnGlobalLayoutListener{
/**
* 縮放手勢的監測
*/
private ScaleGestureDetector mScaleGestureDetector;
/**
* 監聽手勢
*/
private GestureDetector mGestureDetector;
/**
* 對圖片進行縮放平移的Matrix
*/
private Matrix mScaleMatrix;
/**
* 第一次加載圖片時調整圖片縮放比例,使圖片的寬或者高充滿屏幕
*/
private boolean mFirst;
/**
* 圖片的初始化比例
*/
private float mInitScale;
/**
* 圖片的最大比例
*/
private float mMaxScale;
/**
* 雙擊圖片放大的比例
*/
private float mMidScale;
/**
* 是否正在自動放大或者縮小
*/
private boolean isAutoScale;
//-----------------------------------------------
/**
* 上一次觸控點的數量
*/
private int mLastPointerCount;
/**
* 是否可以拖動
*/
private boolean isCanDrag;
/**
* 上一次滑動的x和y坐標
*/
private float mLastX;
private float mLastY;
/**
* 可滑動的臨界值
*/
private int mTouchSlop;
/**
* 是否用檢查左右邊界
*/
private boolean isCheckLeftAndRight;
/**
* 是否用檢查上下邊界
*/
private boolean isCheckTopAndBottom;
public ZoomImageView(Context context) {
this(context, null, 0);
}
public ZoomImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ZoomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//一定要將圖片的ScaleType設置成Matrix類型的
setScaleType(ScaleType.MATRIX);
//初始化縮放手勢監聽器
mScaleGestureDetector = new ScaleGestureDetector(context,this);
//初始化矩陣
mScaleMatrix = new Matrix();
setOnTouchListener(this);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
//初始化手勢檢測器,監聽雙擊事件
mGestureDetector = new GestureDetector(context,new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onDoubleTap(MotionEvent e) {
//如果是正在自動縮放,則直接返回,不進行處理
if (isAutoScale) return true;
//得到點擊的坐標
float x = e.getX();
float y = e.getY();
//如果當前圖片的縮放值小於指定的雙擊縮放值
if (getScale() < mMidScale){
//進行自動放大
post(new AutoScaleRunnable(mMidScale,x,y));
}else{
//當前圖片的縮放值大於初試縮放值,則自動縮小
post(new AutoScaleRunnable(mInitScale,x,y));
}
return true;
}
});
}
/**
* 當view添加到window時調用,早於onGlobalLayout,因此可以在這裡注冊監聽器
*/
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
/**
* 當view從window上移除時調用,因此可以在這裡移除監聽器
*/
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
/**
* 當布局樹發生變化時會調用此方法,我們可以在此方法中獲得控件的寬和高
*/
@Override
public void onGlobalLayout() {
//只有當第一次加載圖片的時候才會進行初始化,用一個變量mFirst控制
if (!mFirst){
mFirst = true;
//得到控件的寬和高
int width = getWidth();
int height = getHeight();
//得到當前ImageView中加載的圖片
Drawable d = getDrawable();
if(d == null){//如果沒有圖片,則直接返回
return;
}
//得到當前圖片的寬和高,圖片的寬和高不一定等於控件的寬和高
//因此我們需要將圖片的寬和高與控件寬和高進行判斷
//將圖片完整的顯示在屏幕中
int dw = d.getIntrinsicWidth();
int dh = d.getIntrinsicHeight();
//我們定義一個臨時變量,根據圖片與控件的寬高比例,來確定這個最終縮放值
float scale = 1.0f;
//如果圖片寬度大於控件寬度,圖片高度小於控件高度
if (dw>width && dh
實現這個功能很簡單,我們先添加一個mMinScale作為可縮小到的最小值,我們指定為初試比例的1/4
/**
* 最小縮放比例
*/
private float mMinScale;
在onGlobalLayout中進行初始化
@Override
public void onGlobalLayout() {
...
//最小縮放比例為初試比例的1/4倍
mMinScale = mInitScale / 4;
...
}
在onScale中,修改如下代碼
@Override
public boolean onScale(ScaleGestureDetector detector) {
...
if ((scaleFactor > 1.0f && scale * scaleFactor < mMaxScale)
|| scaleFactor < 1.0f && scale * scaleFactor > mMinScale){
//邊界控制,如果當前縮放比例乘以scaleFactor之後小於了最小的縮放比例
//我們不允許再縮小
if (scale * scaleFactor < mMinScale + 0.01f){
scaleFactor = mMinScale / scale;
}
...
}
這樣我們的圖片最小就可以縮放到初始化比例的1/4大小了,然後我們還需要添加一個松手後回彈至初試化大小的動畫效果,然後我們需要在onTouch的ACTION_UP中添加如下代碼
@Override
public boolean onTouch(View v, MotionEvent event) {
...
case MotionEvent.ACTION_UP:
//當手指抬起時,將mLastPointerCount置0,停止滑動
mLastPointerCount = 0;
//如果當前圖片大小小於初始化大小
if (getScale() < mInitScale){
//自動放大至初始化大小
post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2));
}
break;
...
}
現在我們看一下效果
這個功能實現起來和上面那個功能基本一致,大家可以先試著自己寫一下。
同理,我們需要先定義一個mMaxOverScale作為放大到最大值後,還能繼續放大到的值。
/**
* 最大溢出值
*/
private float mMaxOverScale;
在onGlobalLayout中進行初始化
@Override
public void onGlobalLayout() {
...
//最大溢出值為最大值的5倍,可以隨意調
mMaxOverScale = mMaxScale * 5;
...
}
在onScale中,修改如下代碼
@Override
public boolean onScale(ScaleGestureDetector detector) {
...
if ((scaleFactor > 1.0f && scale * scaleFactor < mMaxOverScale)
|| scaleFactor < 1.0f && scale * scaleFactor > mMinScale){
if (scale * scaleFactor > mMaxOverScale + 0.01f){
scaleFactor = mMaxOverScale / scale;
}
...
}
這樣當我們圖片放大至最大比例後還可以繼續放大,然後我們同樣需要在onTouch中的ACTION_UP中添加自動縮小的功能
case MotionEvent.ACTION_UP:
//當手指抬起時,將mLastPointerCount置0,停止滑動
mLastPointerCount = 0;
//如果當前圖片大小小於初始化大小
if (getScale() < mInitScale){
//自動放大至初始化大小
post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2));
}
//如果當前圖片大小大於最大值
if (getScale() > mMaxScale){
//自動縮小至最大值
post(new AutoScaleRunnable(mMaxScale,getWidth()/2,getHeight()/2));
}
break;
然後我們看一下效果
要實現圖片的慣性滑動,我們需要借助VelocityTracker來幫我們檢測當我們手指離開圖片時的一個速度,然後根據這個速度以及圖片的位置來調用Scroller的fling方法來計算慣性滑動過程中的x和y的坐標
@Override
public boolean onTouch(View v, MotionEvent event) {
...
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//初始化速度檢測器
mVelocityTracker = VelocityTracker.obtain();
if (mVelocityTracker != null){
//將當前的事件添加到檢測器中
mVelocityTracker.addMovement(event);
}
//當手指再次點擊到圖片時,停止圖片的慣性滑動
if (mFlingRunnable != null){
mFlingRunnable.cancelFling();
mFlingRunnable = null;
}
...
}
...
case MotionEvent.ACTION_MOVE:
...
//如果可滑動
if (isCanDrag){
if (getDrawable() != null){
if (mVelocityTracker != null){
//將當前事件添加到檢測器中
mVelocityTracker.addMovement(event);
}
...
}
...
case MotionEvent.ACTION_UP:
//當手指抬起時,將mLastPointerCount置0,停止滑動
mLastPointerCount = 0;
//如果當前圖片大小小於初始化大小
if (getScale() < mInitScale){
//自動放大至初始化大小
post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2));
}
//如果當前圖片大小大於最大值
if (getScale() > mMaxScale){
//自動縮小至最大值
post(new AutoScaleRunnable(mMaxScale,getWidth()/2,getHeight()/2));
}
if (isCanDrag){//如果當前可以滑動
if (mVelocityTracker != null){
//將當前事件添加到檢測器中
mVelocityTracker.addMovement(event);
//計算當前的速度
mVelocityTracker.computeCurrentVelocity(1000);
//得到當前x方向速度
final float vX = mVelocityTracker.getXVelocity();
//得到當前y方向的速度
final float vY = mVelocityTracker.getYVelocity();
mFlingRunnable = new FlingRunnable(getContext());
//調用fling方法,傳入控件寬高和當前x和y軸方向的速度
//這裡得到的vX和vY和scroller需要的velocityX和velocityY的負號正好相反
//所以傳入一個負值
mFlingRunnable.fling(getWidth(),getHeight(),(int)-vX,(int)-vY);
//執行run方法
post(mFlingRunnable);
}
}
break;
case MotionEvent.ACTION_CANCEL:
//釋放速度檢測器
if (mVelocityTracker != null){
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
/**
* 慣性滑動
*/
private class FlingRunnable implements Runnable{
private Scroller mScroller;
private int mCurrentX , mCurrentY;
public FlingRunnable(Context context){
mScroller = new Scroller(context);
}
public void cancelFling(){
mScroller.forceFinished(true);
}
/**
* 這個方法主要是從onTouch中或得到當前滑動的水平和豎直方向的速度
* 調用scroller.fling方法,這個方法內部能夠自動計算慣性滑動
* 的x和y的變化率,根據這個變化率我們就可以對圖片進行平移了
*/
public void fling(int viewWidth , int viewHeight , int velocityX ,
int velocityY){
RectF rectF = getMatrixRectF();
if (rectF == null){
return;
}
//startX為當前圖片左邊界的x坐標
final int startX = Math.round(-rectF.left);
final int minX , maxX , minY , maxY;
//如果圖片寬度大於控件寬度
if (rectF.width() > viewWidth){
//這是一個滑動范圍[minX,maxX],詳情見下圖
minX = 0;
maxX = Math.round(rectF.width() - viewWidth);
}else{
//如果圖片寬度小於控件寬度,則不允許滑動
minX = maxX = startX;
}
//如果圖片高度大於控件高度,同理
final int startY = Math.round(-rectF.top);
if (rectF.height() > viewHeight){
minY = 0;
maxY = Math.round(rectF.height() - viewHeight);
}else{
minY = maxY = startY;
}
mCurrentX = startX;
mCurrentY = startY;
if (startX != maxX || startY != maxY){
//調用fling方法,然後我們可以通過調用getCurX和getCurY來獲得當前的x和y坐標
//這個坐標的計算是模擬一個慣性滑動來計算出來的,我們根據這個x和y的變化可以模擬
//出圖片的慣性滑動
mScroller.fling(startX,startY,velocityX,velocityY,minX,maxX,minY,maxY);
}
}
關於startX,minX,maxX做一個解釋
我們從圖中可以看出,當前圖片可滑動的一個區間就是左邊多出來的那塊區間,所以minX和maxX代表的是區間的最小值和最大值,startX就是屏幕左邊界的坐標值,我們可以想象成是startX在區間[minX,maxX]的移動。Y軸方向同理。
現在我們看一下效果
完整代碼我會晚些時候上傳
今天我們來講一講Andorid中如何定制返回按鈕的動畫效果。我將結合實際應用來闡述如何使用。首先來看一個效果截圖,有一個搜索按鈕在一個頁面的頂部:我之前實現的方式是和百度
筆者有一段時間沒有發表關於Android的文章了,關於Android自定義組件筆者有好幾篇想跟大家分享的,後期會記錄在博客中。本篇博客給大家分享的是自定義一個日期選擇器,
本文實例講述了Android編程實現換膚功能的方法。分享給大家供大家參考,具體如下:本系列專題培訓適用范圍:初級Android程序員,即有J2SE基礎和Android初級
這一節我們來看看登陸頁面怎樣布局,對於剛接觸到Android開發的童鞋來說,Android的布局感覺比較棘手,需要結合各種屬性進行設置,接下來我們由點入面來 了解安卓中頁