从零开发一个完整的Android项目(九)——图片浏览

图片浏览 包括图片获取、缓存、显示、放大、缩小、拖动、旋转和切换功能。
布局


代码
import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.Rect; import android.hardware.SensorManager; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.OrientationEventListener; import android.view.View; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; import com.example.****.**.Defines; import com.example.****.**.Functions; import com.example.****.**.HttpConnection; import com.example.****.**.MainActivity; import com.example.****.**.R; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; public class StationViewActivity extends AppCompatActivity { private Menu mMenu; private ImageView mImageViewStation; private TextView mTextViewStationName; private ProjectInfo projectInfo; private String projectName; private String stationName; private float mPosX; private float mPosY; private PointF mid = new PointF(); private float oldDist = 1f; private Matrix matrix = new Matrix(); private Matrix matrix1 = new Matrix(); private Matrix savedMatrix = new Matrix(); private boolean mLarger; private float scaleTotal; private float scale; private int mOrientation; private Rect viewRect; private AlbumOrientationEventListener mAlbumOrientationEventListener; Bitmap bitmap; private static final int NONE = 0; private static final int DRAG = 1; private static final int ZOOM = 2; int mode = NONE; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setContentView(R.layout.activity_station_view); HttpConnection.getInstance().setHandler(mHandlerStation); mLarger = false; //新页面接收数据 Bundle bundle = this.getIntent().getExtras(); //接收name值 projectName = bundle.getString("project"); stationName = bundle.getString("station"); projectInfo = ProjectStations.getInstance().getProjectByName(projectName); mTextViewStationName = (TextView) findViewById(R.id.textViewStationName); mTextViewStationName.setText(stationName); mImageViewStation = (ImageView) findViewById(R.id.imageViewStation); mImageViewStation.post(new Runnable() { @Override public void run() { viewRect = new Rect(); mImageViewStation.getGlobalVisibleRect(viewRect); updatePic(); mAlbumOrientationEventListener = new AlbumOrientationEventListener(getBaseContext(), SensorManager.SENSOR_DELAY_NORMAL); if (mAlbumOrientationEventListener.canDetectOrientation()) { mAlbumOrientationEventListener.enable(); } } }); // 实现放大、缩小、拖动和旋转 mImageViewStation.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { // 按下 case MotionEvent.ACTION_DOWN: mPosX = event.getX(); mPosY = event.getY(); mode = DRAG; savedMatrix.set(matrix); break; case MotionEvent.ACTION_POINTER_DOWN: mode = ZOOM; oldDist = spacing(event); savedMatrix.set(matrix); midPoint(mid, event); break; // 移动 case MotionEvent.ACTION_MOVE: if (mode == ZOOM && bitmap != null) { matrix1.set(savedMatrix); float newDist = spacing(event); scale = newDist / oldDist; scale = scale * scaleTotal < 1 ? 1 / scaleTotal : scale; scale = scale * scaleTotal > 5 ? 5 / scaleTotal : scale; matrix1.postScale(scale, scale, mid.x, mid.y); // 縮放 matrix.set(matrix1); mImageViewStation.setImageMatrix(matrix); } else if (mode == DRAG && mLarger) { matrix1.set(savedMatrix); float dx = event.getX() - mPosX; float dy = event.getY() - mPosY; matrix1.postTranslate(dx, dy); // 平移 matrix.set(matrix1); mImageViewStation.setImageMatrix(matrix); } break; // 拿起 case MotionEvent.ACTION_UP: if (mode == DRAG && !mLarger) { if (event.getX() - mPosX > 0 && Math.abs(event.getY() - mPosY) < 100) {// 向右 for (int i = 0; i < projectInfo.stations.size(); ++i) { if (stationName.equals(projectInfo.stations.get(i).name) && i > 0) { stationName = projectInfo.stations.get(i - 1).name; mTextViewStationName.setText(stationName); updatePic(); break; } } } else if (event.getX() - mPosX < 0 && Math.abs(event.getY() - mPosY) < 100) {// 向左 for (int i = 0; i < projectInfo.stations.size(); ++i) { if (stationName.equals(projectInfo.stations.get(i).name) && i < projectInfo.stations.size() - 1) { stationName = projectInfo.stations.get(i + 1).name; mTextViewStationName.setText(stationName); updatePic(); break; } } } else if (event.getY() - mPosY > 0 && Math.abs(event.getX() - mPosX) < 100) {// 向下} else if (event.getY() - mPosY < 0 && Math.abs(event.getX() - mPosX) < 100) {// 向上} } else if (mode == DRAG) { ////复位图片 //PointF p1=getLeftPointF(); //PointF p2=getRightPointF(); // ////左边界复位 //if(p1.x>0) //matrix.postTranslate(-p1.x, 0); ////右边界复位 //if(p2.x < mImageViewStation.getWidth()) //matrix.postTranslate(mImageViewStation.getWidth() - p2.x, 0); ////上边界复位 //if (p1.y > 0) matrix.postTranslate(0, -p1.y); ////下边界复位 //if (p2.y < mImageViewStation.getHeight()) //matrix.postTranslate(0, mImageViewStation.getHeight() - p2.y); ////居中 //if(mImageViewStation.getHeight() > (p2.y - p1.y)) { //float row = (mImageViewStation.getHeight() - (p2.y - p1.y)) / 2; //matrix.postTranslate(0, row - p1.y); //} //if(mImageViewStation.getWidth() > (p2.x - p1.x)){ //float col = (mImageViewStation.getWidth() - (p2.x - p1.x)) / 2; //matrix.postTranslate(col - p1.x, 0); //} //mImageViewStation.setImageMatrix(matrix); } break; case MotionEvent.ACTION_POINTER_UP: if (mode == ZOOM) { scaleTotal *= scale; if (scaleTotal == 1) { float scale = (float) (viewRect.width() * 1.0 / bitmap.getWidth()); matrix = new Matrix(); matrix.postScale(scale, scale); matrix.postTranslate(0, (viewRect.height() - bitmap.getHeight() * scale) / 2); matrix.postRotate(360 - mOrientation, viewRect.width() / 2, viewRect.height() / 2); // 旋轉 mImageViewStation.setImageMatrix(matrix); scaleTotal = 1f; } mLarger = scaleTotal > 1; } mode = NONE; break; default: break; } return true; } }); }@Override protected void onDestroy() { mAlbumOrientationEventListener.disable(); super.onDestroy(); }// 触碰两点间距离 private float spacing(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y); }// 取手势中心点 private void midPoint(PointF point, MotionEvent event) { float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); }//获取图片的上坐标 private PointF getLeftPointF() { float[] values = new float[9]; matrix.getValues(values); float leftX = values[2]; float leftY = values[5]; return new PointF(leftX, leftY); }//获取图片的下坐标 private PointF getRightPointF() { Rect rectTemp = mImageViewStation.getDrawable().getBounds(); float[] values = new float[9]; matrix.getValues(values); float leftX = values[2] + rectTemp.width() * values[0]; float leftY = values[5] + rectTemp.height() * values[4]; return new PointF(leftX, leftY); }// 根据手机方向,旋转图片 private class AlbumOrientationEventListener extends OrientationEventListener { public AlbumOrientationEventListener(Context context) { super(context); }public AlbumOrientationEventListener(Context context, int rate) { super(context, rate); }@Override public void onOrientationChanged(int orientation) { if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) { return; }//保证只返回四个方向 int newOrientation = ((orientation + 45) / 90 * 90) % 360; if (newOrientation != mOrientation) { //返回的mOrientation就是手机方向,为0°、90°、180°和270°中的一个 matrix.postRotate(mOrientation - newOrientation, viewRect.width() / 2, viewRect.height() / 2); // 旋轉 mImageViewStation.setImageMatrix(matrix); mOrientation = newOrientation; } } }// 显示图片 private void updatePic() { String filePath = this.getExternalCacheDir().getAbsolutePath() + "/" + projectName + "-" + stationName + ".jpg"; File file = new File(filePath); if (file.exists()) { bitmap = BitmapFactory.decodeFile(filePath); float scale = (float) (viewRect.width() * 1.0 / bitmap.getWidth()); matrix = new Matrix(); matrix.postScale(scale, scale); matrix.postTranslate(0, (viewRect.height() - bitmap.getHeight() * scale) / 2); mImageViewStation.setImageBitmap(bitmap); mImageViewStation.setImageMatrix(matrix); scaleTotal = 1f; } else { HttpConnection.getInstance().Add(Defines.REQUEST_GET, Defines.INTERFACE_GET_GRAY, projectName + "/" + stationName + "/"); Functions.showProgressDialog(StationViewActivity.this, getResources().getString(R.string.loading)); } }// 保存Bitmap图片到本地 private void saveMyBitmap(Bitmap mBitmap, String path) { File f = new File(path); FileOutputStream fOut = null; try { fOut = new FileOutputStream(f); } catch (FileNotFoundException e) { e.printStackTrace(); } mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fOut); try { if (fOut != null) { fOut.flush(); fOut.close(); } } catch (IOException e) { e.printStackTrace(); } }// 保存通过HTTP通信获取的图片 private void setGrayPic(String strGrayPic) { if (strGrayPic != null && !strGrayPic.equals("") && !strGrayPic.equals("success")) { try { byte[] byteGrayPic = strGrayPic.getBytes("ISO-8859-1"); Bitmap bitmap = BitmapFactory.decodeByteArray(byteGrayPic, 0, byteGrayPic.length); String filePath = this.getExternalCacheDir().getAbsolutePath() + "/" + projectName + "-" + stationName + ".jpg"; saveMyBitmap(bitmap, filePath); updatePic(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else { Resources res = getResources(); bitmap = BitmapFactory.decodeResource(res, R.drawable.****); float scale = (float) (viewRect.width() * 1.0 / bitmap.getWidth()); matrix = new Matrix(); matrix.postScale(scale, scale); matrix.postTranslate(0, (viewRect.height() - bitmap.getHeight() * scale) / 2); mImageViewStation.setImageBitmap(bitmap); mImageViewStation.setImageMatrix(matrix); scaleTotal = 1f; } Functions.cancelProgressDialog(); }public Handler mHandlerStation = new Handler() { public void handleMessage(Message msg) { String strRecv = msg.getData().getString("Body"); switch (msg.what) { case Defines.ERROR: int statusCode = msg.getData().getInt("Status"); new android.support.v7.app.AlertDialog.Builder(StationViewActivity.this).setTitle( getResources().getString(R.string.error) + "(Error code:" + String.valueOf(statusCode) + ")") .setIcon(android.R.drawable.ic_dialog_info) .setCancelable(false) .setPositiveButton(getResources().getString(R.string.close), new DialogInterface.OnClickListener() {@Override public void onClick(DialogInterface dialog, int which) { // 点击“确认”后的操作 Intent intent = new Intent(StationViewActivity.this, MainActivity.class); //传递退出所有Activity的Tag对应的布尔值为true intent.putExtra("exist", true); //启动BaseActivity startActivity(intent); } }).show(); break; case Defines.INTERFACE_GET_GRAY: setGrayPic(strRecv); break; default: break; } super.handleMessage(msg); } }; }


说明
  • 实现了在不自定义ImageView的情况下,图片显示、放大、缩小、拖动、旋转和切换功能;
  • 布局很简单,一个ImageView和一个TextView,ImageView设置android:scaleType="matrix",代码中通过设置matrix改变图片显示;
  • 采用多点触摸放大、缩小,放大的时候滑动是拖动效果,不放大的时候是切换功能;
  • 根据手机方向,旋转图片;
  • 最开始是想限制拖动范围,后面没有找到好的方法就放弃;

    推荐阅读