編輯:關於Android編程
作為Android開發者,我們經常需要自定義控件,比如下面我們說的實現圖片的多點觸控和伸縮釋放,這也是由於用戶已經有這樣的常識了,那就是看見有圖片的地方就可以點擊查看大圖,並且可以通過手指對圖片進行伸縮和移動,如果應用沒有實現這一點,那麼對用戶來說將會是很糟糕的體驗,用戶很“憤怒”。所以作為Android開發者,我們的任務就是讓用戶“爽”。哈哈哈。。。。下面我們將通過自定義ImageView實現以上功能。
一、Matrix(矩陣),Android是通過Matrix去控制圖片的伸縮和平移的。
二、ScaleGestureDetector(伸縮手勢探測器),實現對用戶操作圖片伸縮的監聽。
一、創建ZoomImageView類,通過重寫ImageView的OnAttachedToWindow()注冊全局布局監聽器getViewTreeObserver().addOnGlobalLayoutListener(this),實現對圖片的初始化控制
二、在全局布局監聽器裡面通過Matrix控制初始化圖片居中顯示和縮放。
三、注冊onTouch()事件,通過代碼mScaleGestureDetector.onTouchEvent(motionEvent) ;實現ScaleGestureDetector監聽手勢。
四、在ScaleGestureDetector的回調方法onScale(ScaleGestureDetector detector)裡面實現對根據用戶的操作,實現對圖片的伸縮和移動。
package com.liujun.liujunzoomimagedemo.view;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.view.ViewTreeObserver;
public class ZoomImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener, OnScaleGestureListener, OnTouchListener {
private ScaleGestureDetector mScaleGestureDetector = null;
private Matrix matrix;
private float[] matrixValues = new float[9];// 矩陣的九個值
private boolean isOnceLayout = true;
// 設置縮放比
private float initScale = 1.0f;
private float midScale = 2.0f;
private float maxScale = 4.0f;
public ZoomImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
super.setScaleType(ScaleType.MATRIX);// 設置圖片通過矩陣控制
// 實例化伸縮手勢探測器
mScaleGestureDetector = new ScaleGestureDetector(context, this);
matrix = new Matrix();
this.setOnTouchListener(this);
}
public ZoomImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ZoomImageView(Context context) {
this(context, null);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// 注冊全局布局監聽器
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@SuppressWarnings("deprecation")
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
// 取消全局布局監聽器
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
/**
* 獲取布局的參數(這個方法會調用兩次)
*/
@Override
public void onGlobalLayout() {
if (isOnceLayout) {// 將圖片居中顯示,並且伸縮圖片
Drawable drawable = this.getDrawable();
if (drawable == null) {
return;
}
// 獲取父控制的寬高
int parentWidgetWidth = this.getWidth();
int parentWidgetHeight = this.getHeight();
// 獲取圖片的寬高
int drawableHeight = drawable.getIntrinsicHeight();
int drawableWidth = drawable.getIntrinsicWidth();
// 定義縮放比
float scale = 1.0f;
// 當圖片寬度大於父控件的寬度,當高度小於父控件高度時(縮小)
if (drawableWidth > parentWidgetWidth && drawableHeight <= parentWidgetHeight) {
scale = parentWidgetWidth * 1.0f / drawableWidth;
}
// 當圖片高度高於父控件,但寬度小於父控件時(縮小)
if (drawableHeight > parentWidgetHeight && drawableWidth <= parentWidgetWidth) {
scale = parentWidgetHeight * 1.0f / drawableHeight;
}
// 當圖片寬度和高度都大於父控件時(縮小)
if (drawableHeight > parentWidgetHeight && drawableWidth > parentWidgetWidth) {
scale = Math.min(parentWidgetHeight * 1.0f / drawableHeight, parentWidgetWidth * 1.0f / drawableWidth);
}
// 當圖片寬度和高度都小於父控件(擴大)
if (drawableHeight < parentWidgetHeight && drawableWidth < parentWidgetWidth) {
scale = Math.min(parentWidgetHeight * 1.0f / drawableHeight, parentWidgetWidth * 1.0f / drawableWidth);
}
// 設置初始化的縮放比
initScale = scale;
// 將圖片縮放並且將圖片移動到父控件中間
float dx = (parentWidgetWidth - drawableWidth) / 2;
float dy = (parentWidgetHeight - drawableHeight) / 2;
matrix.postTranslate(dx, dy);
// 將圖片縮放
matrix.postScale(scale, scale, parentWidgetWidth / 2, parentWidgetHeight / 2);
// 將矩陣運用到圖片中
this.setImageMatrix(matrix);
isOnceLayout = false;
}
}
/**
* 獲取圖片當前的縮放比
*
* @return
*/
private float getCurrentImageScale() {
matrix.getValues(matrixValues);
return matrixValues[Matrix.MSCALE_X];
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
// 獲取圖片當前的縮放比
float currentScalse = getCurrentImageScale();
// 拿到圖片將要的縮放比例
float scaleFactor = detector.getScaleFactor();
if (this.getDrawable() == null) {
return true;
}
// 用戶將要放大圖片或者用戶將要縮小圖片
if ((scaleFactor > 1.0f && currentScalse < maxScale) || (scaleFactor < 1.0f && currentScalse > initScale)) {
// 縮小時
if (scaleFactor * currentScalse < initScale) {
scaleFactor = initScale / currentScalse;
}
// 放大時
if (scaleFactor * currentScalse > maxScale) {
scaleFactor = maxScale / currentScalse;
}
matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
// 檢查邊界和中心點
checkBorderAndCenterWhenScale();
setImageMatrix(matrix);
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
/**
* 當在縮放的時候,對圖片的邊界和中心進行控制
*/
private void checkBorderAndCenterWhenScale() {
// 獲取當前縮放過程中的圖片的矩形
RectF rectF = getMatrixRectF();
float deltaX = 0;
float deltaY = 0;
// 獲取父控件的寬高
int parentWidth = getWidth();
int parentHeight = getHeight();
// 如果寬度大於屏幕寬度
if (rectF.width() >= parentWidth) {
if (rectF.left > 0) {// 左邊出現了空白
deltaX = -rectF.left;// 往左移動
}
if (rectF.right < parentWidth) {// 右邊出現了空白
deltaX = parentWidth - rectF.right;// 往右移動
}
}
// 如果高度大於屏幕高度
if (rectF.height() >= parentHeight) {
if (rectF.top > 0) {// 上邊出現了空白
deltaY = -rectF.top;// 往下移動
}
if (rectF.bottom < parentHeight) {// 下面出現了空白
deltaY = parentHeight - rectF.bottom;// 往下移動
}
}
// 如果寬度小於父控件的寬度
if (rectF.width() < parentWidth) {// 要基中顯示
deltaX = parentWidth * 0.5f - rectF.right + 0.5f * rectF.width();
}
// 如果高度消息小於父控件的高度
if (rectF.height() < parentHeight) {// 需要基中顯示
deltaY = parentHeight * 0.5f - rectF.bottom + 0.5f * rectF.height();
}
// 將圖片移動到父控件中心
matrix.postTranslate(deltaX, deltaY);
}
/**
* 獲取圖片通過矩陣控制縮放之後的矩形
*
* @return
*/
private RectF getMatrixRectF() {
Matrix matrix2 = matrix;
RectF rectF = new RectF();
Drawable drawable = this.getDrawable();
if (drawable != null) {
rectF.set(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
matrix2.mapRect(rectF);
}
return rectF;
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
// 用戶縮放手機探測器處理觸摸事件
mScaleGestureDetector.onTouchEvent(motionEvent);
return true;
}
}
實現效果圖
對圖片的伸縮釋放已經實現了,但是相信讀者也能夠知道,單單只實現多點觸控自由伸縮是不夠的,用戶還希望可以左右滑動實現圖片切換浏覽,該部分的實現將在近期發布,盡情期待。
代碼下載地址
Android實現圖片異步加載及本地緩存
在android項目中訪問網絡圖片是非常普遍性的事情,如果我們每次請求都要訪問網絡來獲取圖片,會非常耗費流量,而且圖片占用內存空間也比較大,圖片過多且不釋放的話很容易造成
Android Studio之基本Gradle使用
Android Studio的一大特色就是自動構建工具gradle的使用。1.配置Gradle環境變量下載最新Gradle整包下載地址:http://www.androi
Android開發自定義View實現數字與圖片無縫切換的2048
最近在學自定義View,無意中看到鴻洋大神以前寫過的2048,看起來很不錯,所以自己在他的基礎上做一個加強版的2048。先看圖: 功能除了正常的2048外,還支
Android----SpannableString
1、背景介紹在開發應用過程中經常會遇到顯示一些不同的字體風格的信息猶如默認的LockScreen上面的時間和充電信息。對於類似的情況,可能第一反應就是用不同的多個Text