天天看点

Android之——根据手势简单缩放图片

很久没有发表关于Android的文章了,今天,给大家带来一篇如何根据手势简单缩放图片的文章。

一、原理

老样子,我们还是先讲讲原理性的东东,很简单,当手指触摸屏幕图片时,记录下当时手指的坐标点;手指滑动的时,计算手指之间的距离,如果手指之间的距离增大,则放大图片,如果手指之间的距离缩小,则缩小图片的显示。

二、实现

1、自定义ImageView

创建类DragImageView继承自ImageView,这个类是我们这个示例程序的核心类,在这个类中封装了手指触摸事件,滑动事件,手指离开事件等监听动作,同时在滑动事件中计算手指之间的距离,从而根据距离的放大与缩小来放大和缩小图片。

具体实现代码如下:

package com.lyz.image.drag;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.animation.ScaleAnimation;
import android.widget.ImageView;

/**
 * 自定义ImageView实现图片的缩放功能
 * @author liuyazhuang
 *
 */
public class DragImageView extends ImageView {

  private Activity mActivity;

  private int screen_W, screen_H;

  private int bitmap_W, bitmap_H;

  private int MAX_W, MAX_H, MIN_W, MIN_H;

  private int current_Top, current_Right, current_Bottom, current_Left;

  private int start_Top = -1, start_Right = -1, start_Bottom = -1,
      start_Left = -1;

  private int start_x, start_y, current_x, current_y;

  private float beforeLenght, afterLenght;

  private float scale_temp;

  private enum MODE {
    NONE, DRAG, ZOOM

  };

  private MODE mode = MODE.NONE;

  private boolean isControl_V = false;

  private boolean isControl_H = false;

  private ScaleAnimation scaleAnimation;

  private boolean isScaleAnim = false;

  private MyAsyncTask myAsyncTask;

  public DragImageView(Context context) {
    super(context);
  }

  public void setmActivity(Activity mActivity) {
    this.mActivity = mActivity;
  }

  public void setScreen_W(int screen_W) {
    this.screen_W = screen_W;
  }

  public void setScreen_H(int screen_H) {
    this.screen_H = screen_H;
  }

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

  
  @Override
  public void setImageBitmap(Bitmap bm) {
    super.setImageBitmap(bm);
    bitmap_W = bm.getWidth();
    bitmap_H = bm.getHeight();

    MAX_W = bitmap_W * 3;
    MAX_H = bitmap_H * 3;

    MIN_W = bitmap_W / 2;
    MIN_H = bitmap_H / 2;

  }

  @Override
  protected void onLayout(boolean changed, int left, int top, int right,
      int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    if (start_Top == -1) {
      start_Top = top;
      start_Left = left;
      start_Bottom = bottom;
      start_Right = right;
    }

  }

  
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
    case MotionEvent.ACTION_DOWN:
      onTouchDown(event);
      break;
    case MotionEvent.ACTION_POINTER_DOWN:
      onPointerDown(event);
      break;

    case MotionEvent.ACTION_MOVE:
      onTouchMove(event);
      break;
    case MotionEvent.ACTION_UP:
      mode = MODE.NONE;
      break;

    case MotionEvent.ACTION_POINTER_UP:
      mode = MODE.NONE;
      if (isScaleAnim) {
        doScaleAnim();
      }
      break;
    }

    return true;
  }

  void onTouchDown(MotionEvent event) {
    mode = MODE.DRAG;

    current_x = (int) event.getRawX();
    current_y = (int) event.getRawY();

    start_x = (int) event.getX();
    start_y = current_y - this.getTop();

  }

  void onPointerDown(MotionEvent event) {
    if (event.getPointerCount() == 2) {
      mode = MODE.ZOOM;
      beforeLenght = getDistance(event);
    }
  }

  void onTouchMove(MotionEvent event) {
    int left = 0, top = 0, right = 0, bottom = 0;
    if (mode == MODE.DRAG) {


      left = current_x - start_x;
      right = current_x + this.getWidth() - start_x;
      top = current_y - start_y;
      bottom = current_y - start_y + this.getHeight();

      if (isControl_H) {
        if (left >= 0) {
          left = 0;
          right = this.getWidth();
        }
        if (right <= screen_W) {
          left = screen_W - this.getWidth();
          right = screen_W;
        }
      } else {
        left = this.getLeft();
        right = this.getRight();
      }
      if (isControl_V) {
        if (top >= 0) {
          top = 0;
          bottom = this.getHeight();
        }

        if (bottom <= screen_H) {
          top = screen_H - this.getHeight();
          bottom = screen_H;
        }
      } else {
        top = this.getTop();
        bottom = this.getBottom();
      }
      if (isControl_H || isControl_V)
        this.setPosition(left, top, right, bottom);

      current_x = (int) event.getRawX();
      current_y = (int) event.getRawY();

    }
    else if (mode == MODE.ZOOM) {

      afterLenght = getDistance(event);

      float gapLenght = afterLenght - beforeLenght;

      if (Math.abs(gapLenght) > 5f) {
        scale_temp = afterLenght / beforeLenght;

        this.setScale(scale_temp);

        beforeLenght = afterLenght;
      }
    }

  }

  /**
   * 计算距离
   * @param event
   * @return
   */
  float getDistance(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);

    return FloatMath.sqrt(x * x + y * y);
  }

  /**
   * 设置位置坐标
   * @param left
   * @param top
   * @param right
   * @param bottom
   */
  private void setPosition(int left, int top, int right, int bottom) {
    this.layout(left, top, right, bottom);
  }

  void setScale(float scale) {
    int disX = (int) (this.getWidth() * Math.abs(1 - scale)) / 4;
    int disY = (int) (this.getHeight() * Math.abs(1 - scale)) / 4;

    if (scale > 1 && this.getWidth() <= MAX_W) {
      current_Left = this.getLeft() - disX;
      current_Top = this.getTop() - disY;
      current_Right = this.getRight() + disX;
      current_Bottom = this.getBottom() + disY;

      this.setFrame(current_Left, current_Top, current_Right,
          current_Bottom);
      if (current_Top <= 0 && current_Bottom >= screen_H) {
        isControl_V = true;
      } else {
        isControl_V = false;
      }
      if (current_Left <= 0 && current_Right >= screen_W) {
        isControl_H = true;
      } else {
        isControl_H = false;
      }

    }
    else if (scale < 1 && this.getWidth() >= MIN_W) {
      current_Left = this.getLeft() + disX;
      current_Top = this.getTop() + disY;
      current_Right = this.getRight() - disX;
      current_Bottom = this.getBottom() - disY;
      if (isControl_V && current_Top > 0) {
        current_Top = 0;
        current_Bottom = this.getBottom() - 2 * disY;
        if (current_Bottom < screen_H) {
          current_Bottom = screen_H;
          isControl_V = false;
        }
      }
      if (isControl_V && current_Bottom < screen_H) {
        current_Bottom = screen_H;
        current_Top = this.getTop() + 2 * disY;
        if (current_Top > 0) {
          current_Top = 0;
          isControl_V = false;
        }
      }

      if (isControl_H && current_Left >= 0) {
        current_Left = 0;
        current_Right = this.getRight() - 2 * disX;
        if (current_Right <= screen_W) {
          current_Right = screen_W;
          isControl_H = false;
        }
      }
      if (isControl_H && current_Right <= screen_W) {
        current_Right = screen_W;
        current_Left = this.getLeft() + 2 * disX;
        if (current_Left >= 0) {
          current_Left = 0;
          isControl_H = false;
        }
      }

      if (isControl_H || isControl_V) {
        this.setFrame(current_Left, current_Top, current_Right,
            current_Bottom);
      } else {
        this.setFrame(current_Left, current_Top, current_Right,
            current_Bottom);
        isScaleAnim = true;
      }

    }

  }


  public void doScaleAnim() {
    myAsyncTask = new MyAsyncTask(screen_W, this.getWidth(),
        this.getHeight());
    myAsyncTask.setLTRB(this.getLeft(), this.getTop(), this.getRight(),
        this.getBottom());
    myAsyncTask.execute();
    isScaleAnim = false;
  }

  class MyAsyncTask extends AsyncTask<Void, Integer, Void> {
    private int screen_W, current_Width, current_Height;

    private int left, top, right, bottom;

    private float scale_WH;

    public void setLTRB(int left, int top, int right, int bottom) {
      this.left = left;
      this.top = top;
      this.right = right;
      this.bottom = bottom;
    }

    private float STEP = 8f;

    private float step_H, step_V;

    public MyAsyncTask(int screen_W, int current_Width, int current_Height) {
      super();
      this.screen_W = screen_W;
      this.current_Width = current_Width;
      this.current_Height = current_Height;
      scale_WH = (float) current_Height / current_Width;
      step_H = STEP;
      step_V = scale_WH * STEP;
    }

    @Override
    protected Void doInBackground(Void... params) {

      while (current_Width <= screen_W) {

        left -= step_H;
        top -= step_V;
        right += step_H;
        bottom += step_V;

        current_Width += 2 * step_H;

        left = Math.max(left, start_Left);
        top = Math.max(top, start_Top);
        right = Math.min(right, start_Right);
        bottom = Math.min(bottom, start_Bottom);
               //Log.e("jj", "top="+top+",bottom="+bottom+",left="+left+",right="+right);
        onProgressUpdate(new Integer[] { left, top, right, bottom });
        try {
          Thread.sleep(10);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }

      return null;
    }

    @Override
    protected void onProgressUpdate(final Integer... values) {
      super.onProgressUpdate(values);
      mActivity.runOnUiThread(new Runnable() {
        @Override
        public void run() {
          setFrame(values[0], values[1], values[2], values[3]);
        }
      });

    }
  }
}      

2、图片处理的工具类BitmapUtil

这个类的主要作用是对图片进行基本的处理操作,比如压缩等等,具体功能可见代码注释。

具体实现代码如下:

package com.lyz.image.drag;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLEncoder;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Environment;
import android.os.StatFs;
import android.util.Log;

/**
 * Bitmap的工具类
 * @author liuyazhuang
 *
 */
public class BitmapUtil {
  /**
   * 读取本地资源的图片
   * @param context
   * @param resId
   * @return
   */
  public static Bitmap ReadBitmapById(Context context, int resId) {
    BitmapFactory.Options opt = new BitmapFactory.Options();
    opt.inPreferredConfig = Bitmap.Config.RGB_565;
    opt.inPurgeable = true;
    opt.inInputShareable = true;
    // 获取资源图片
    InputStream is = context.getResources().openRawResource(resId);
    return BitmapFactory.decodeStream(is, null, opt);
  }

  /***
   * 根据资源文件获取Bitmap
   * 
   * @param context
   * @param drawableId
   * @return
   */
  public static Bitmap ReadBitmapById(Context context, int drawableId,
      int screenWidth, int screenHight) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Config.ARGB_8888;
    options.inInputShareable = true;
    options.inPurgeable = true;
    InputStream stream = context.getResources().openRawResource(drawableId);
    Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
    return getBitmap(bitmap, screenWidth, screenHight);
  }

  /***
   * 等比例压缩图片
   * 
   * @param bitmap
   * @param screenWidth
   * @param screenHight
   * @return
   */
  public static Bitmap getBitmap(Bitmap bitmap, int screenWidth,
      int screenHight) {
    int w = bitmap.getWidth();
    int h = bitmap.getHeight();
    Log.e("jj", "图片宽度" + w + ",screenWidth=" + screenWidth);
    Matrix matrix = new Matrix();
    float scale = (float) screenWidth / w;
    float scale2 = (float) screenHight / h;

    // scale = scale < scale2 ? scale : scale2;

    // 保证图片不变形.
    matrix.postScale(scale, scale);
    // w,h是原图的属性.
    return Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
  }

  /***
   * 保存图片至SD卡
   * 
   * @param bm
   * @param url
   * @param quantity
   */
  private static int FREE_SD_SPACE_NEEDED_TO_CACHE = 1;
  private static int MB = 1024 * 1024;
  public final static String DIR = "/sdcard/hypers";

  public static void saveBmpToSd(Bitmap bm, String url, int quantity) {
    // 判断sdcard上的空间
    if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
      return;
    }
    if (!Environment.MEDIA_MOUNTED.equals(Environment
        .getExternalStorageState()))
      return;
    String filename = url;
    // 目录不存在就创建
    File dirPath = new File(DIR);
    if (!dirPath.exists()) {
      dirPath.mkdirs();
    }

    File file = new File(DIR + "/" + filename);
    try {
      file.createNewFile();
      OutputStream outStream = new FileOutputStream(file);
      bm.compress(Bitmap.CompressFormat.PNG, quantity, outStream);
      outStream.flush();
      outStream.close();

    } catch (FileNotFoundException e) {

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

  }

  /***
   * 获取SD卡图片
   * 
   * @param url
   * @param quantity
   * @return
   */
  public static Bitmap GetBitmap(String url, int quantity) {
    InputStream inputStream = null;
    String filename = "";
    Bitmap map = null;
    URL url_Image = null;
    String LOCALURL = "";
    if (url == null)
      return null;
    try {
      filename = url;
    } catch (Exception err) {
    }

    LOCALURL = URLEncoder.encode(filename);
    if (Exist(DIR + "/" + LOCALURL)) {
      map = BitmapFactory.decodeFile(DIR + "/" + LOCALURL);
    } else {
      try {
        url_Image = new URL(url);
        inputStream = url_Image.openStream();
        map = BitmapFactory.decodeStream(inputStream);
        // url = URLEncoder.encode(url, "UTF-8");
        if (map != null) {
          saveBmpToSd(map, LOCALURL, quantity);
        }
        inputStream.close();
      } catch (Exception e) {
        e.printStackTrace();
        return null;
      }
    }
    return map;
  }

  /***
   * 判断图片是存在
   * 
   * @param url
   * @return
   */
  public static boolean Exist(String url) {
    File file = new File(DIR + url);
    return file.exists();
  }

  /** * 计算sdcard上的剩余空间 * @return */
  private static int freeSpaceOnSd() {
    StatFs stat = new StatFs(Environment.getExternalStorageDirectory()
        .getPath());
    double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat
        .getBlockSize()) / MB;

    return (int) sdFreeMB;
  }

}      

3、界面布局main.xml

这个布局文件很简单,只是加载我们自己自定义的DragImageView

具体代码如下:

<?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:gravity="center" >
    
    <com.lyz.image.drag.DragImageView
        android:id="@+id/div_main"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:scaleType="fitXY"
        />

</LinearLayout>      

4、MainActivity

这个类是我们整个程序的入口,通过这个程序加载我们的main.xml布局文件,来实现我们整个应用程序的功能。

package com.lyz.image.drag;

import java.io.InputStream;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.view.WindowManager;

/**
 * 测试图片按手势进行缩放
 * @author liuyazhuang
 *
 */
public class MainActivity extends Activity {
  private int window_width, window_height;// 控件宽度
  private DragImageView dragImageView;// 自定义控件
  private int state_height;// 状态栏的高度

  private ViewTreeObserver viewTreeObserver;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
     requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.main);
    /** 获取可見区域高度 **/
    WindowManager manager = getWindowManager();
    window_width = manager.getDefaultDisplay().getWidth();
    window_height = manager.getDefaultDisplay().getHeight();

    dragImageView = (DragImageView) findViewById(R.id.div_main);
    Bitmap bmp = BitmapUtil.ReadBitmapById(this, R.drawable.huoying,
        window_width, window_height);
    // 设置图片
    dragImageView.setImageBitmap(bmp);
    dragImageView.setmActivity(this);//注入Activity.
    /** 测量状态栏高度 **/
    viewTreeObserver = dragImageView.getViewTreeObserver();
    viewTreeObserver
        .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

          @Override
          public void onGlobalLayout() {
            if (state_height == 0) {
              // 获取状况栏高度
              Rect frame = new Rect();
              getWindow().getDecorView()
                  .getWindowVisibleDisplayFrame(frame);
              state_height = frame.top;
              dragImageView.setScreen_H(window_height-state_height);
              dragImageView.setScreen_W(window_width);
            }

          }
        });

  }

  /**
   * 读取本地资源的图片
   * 
   * @param context
   * @param resId
   * @return
   */
  public static Bitmap ReadBitmapById(Context context, int resId) {
    BitmapFactory.Options opt = new BitmapFactory.Options();
    opt.inPreferredConfig = Bitmap.Config.RGB_565;
    opt.inPurgeable = true;
    opt.inInputShareable = true;
    // 获取资源图片
    InputStream is = context.getResources().openRawResource(resId);
    return BitmapFactory.decodeStream(is, null, opt);
  }

}      

5、AndroidManifest.xml

这个文件我们没有修改它,所有的内容都是创建Android项目的时候自动生成的。

具体代码如下:

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

    <uses-sdk android:minSdkVersion="10" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name="com.lyz.image.drag.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>
    </application>

</manifest>      

三、运行效果

Android之——根据手势简单缩放图片
Android之——根据手势简单缩放图片

四、温馨提示