Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發筆記(九十四)圖片的基本加工

Android開發筆記(九十四)圖片的基本加工

編輯:關於Android編程

位圖管理Bitmap

Android上的圖形使用Drawable類,而位圖管理則使用Bitmap類,java上與之對應的是awt包中的BufferedImage。Android開發中有需要對jpg、png文件進行加工的,都是操作Bitmap,下面是Bitmap類的常用方法說明:
compress : 根據設定的位圖格式與壓縮質量,對圖片進行壓縮。
recycle : 回收位圖對象資源。
createBitmap : 從源圖片中裁剪一塊位圖區域。
createScaledBitmap : 根據設定的目標大小,對源圖片進行縮放。
getByteCount : 獲取位圖的字節大小。
getWidth : 獲取位圖的寬度。
getHeight : 獲取位圖的高度。


圖片讀寫

圖片文件的讀寫,其實就是Bitmap對象與圖片文件的轉換操作,下面是圖片文件讀寫的示例代碼:
	public static void saveBitmap(String path, Bitmap bitmap) {
		try {
			BufferedOutputStream bos = new BufferedOutputStream(
					new FileOutputStream(path));
			bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos);
			bos.flush();
			bos.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static Bitmap openBitmap(String path) {
		Bitmap bitmap = null;
		try {
			BufferedInputStream bis = new BufferedInputStream(
					new FileInputStream(path));
			bitmap = BitmapFactory.decodeStream(bis);
			bis.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bitmap;
	}


至於Bitmap與Drawable之間的轉換,則是通過BitmapDrawable來完成,其中Bitmap轉Drawable的代碼例子如下:
Drawable mDrawable = new BitmapDrawable(getResources(), bitmap);
Drawable轉Bitmap的代碼例子如下:
Bitmap bitmap = ((BitmapDrawable)mDrawable).getBitmap();


圖片加工

常用的圖片加工操作有:圖片壓縮、調整大小、圖片裁剪、圖片旋轉等等。下面是圖片壓縮、調整大小、圖片裁剪的使用說明:


圖片壓縮

壓縮算法主要有兩個因素,一個是圖片格式,另一個是壓縮質量,圖片壓縮可調用compress方法來實現。下面是圖片壓縮的代碼例子:
import java.util.Locale;
import java.util.Map;

import com.example.exmimage.dialog.FileSaveFragment;
import com.example.exmimage.dialog.FileSaveFragment.FileSaveCallbacks;
import com.example.exmimage.dialog.FileSelectFragment;
import com.example.exmimage.dialog.FileSelectFragment.FileSelectCallbacks;
import com.example.exmimage.util.BitmapUtil;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.Toast;
import android.widget.AdapterView.OnItemSelectedListener;

public class CompressActivity extends Activity implements OnClickListener
	,OnLongClickListener,FileSelectCallbacks,FileSaveCallbacks {
	
	private ImageView iv_compress;
	private EditText et_quality;
	private Drawable mDrawable = null;
	private String mExtesion;
	private int mQuality;

	private String[] extensionArray = {"jpg", "png"};
	class ExtensionSelectedListener implements OnItemSelectedListener {
		public void onItemSelected(AdapterView arg0, View arg1, int arg2, long arg3) {
			mExtesion = extensionArray[arg2];
		}

		public void onNothingSelected(AdapterView arg0) {
		}
	}
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_compress);
		
		Button btn_open_drawable = (Button) findViewById(R.id.btn_open_drawable);
		Button btn_save_drawable = (Button) findViewById(R.id.btn_save_drawable);
		btn_open_drawable.setOnClickListener(this);
		btn_save_drawable.setOnClickListener(this);
		
		iv_compress = (ImageView) findViewById(R.id.iv_compress);
		et_quality = (EditText) findViewById(R.id.et_quality);
		et_quality.setOnLongClickListener(this);
		ArrayAdapter extensionAdapter = new ArrayAdapter(this,
				R.layout.spinner_item, extensionArray);
		extensionAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
		Spinner sp = (Spinner) findViewById(R.id.sp_format);
		sp.setPrompt("請選擇圖片格式");
		sp.setAdapter(extensionAdapter);
		sp.setOnItemSelectedListener(new ExtensionSelectedListener());
		sp.setSelection(0);
	}

	@Override
	public boolean onLongClick(View v) {
		if (v.getId() == R.id.et_quality) {
			et_quality.setText("");
		}
		return true;
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == R.id.btn_open_drawable) {
			FileSelectFragment.show(this, new String[]{"jpg","png"}, null);
		} else if (v.getId() == R.id.btn_save_drawable) {
			if (mDrawable == null) {
				Toast.makeText(this, "請先打開圖片文件", Toast.LENGTH_LONG).show();
				return;
			}
			mQuality = Integer.parseInt(et_quality.getText().toString());
			if (mQuality>100 || mQuality<10) {
				Toast.makeText(this, "圖片質量有效值為10-100", Toast.LENGTH_LONG).show();
				return;
			}
			FileSaveFragment.show(this, mExtesion);
		}
	}

	@Override
	public boolean onCanSave(String absolutePath, String fileName) {
		return true;
	}

	@Override
	public void onConfirmSave(String absolutePath, String fileName) {
		String path = String.format("%s/%s", absolutePath, fileName);
		Bitmap bitmap = ((BitmapDrawable)mDrawable).getBitmap();
		BitmapUtil.saveBitmap(path, bitmap, mExtesion, mQuality);
		Toast.makeText(this, "成功保存圖片文件:"+path, Toast.LENGTH_LONG).show();
	}

	@Override
	public void onConfirmSelect(String absolutePath, String fileName,
			Map map_param) {
		String extension = fileName.substring(fileName.lastIndexOf(".")+1);
		if (extension.toUpperCase(Locale.getDefault()).equals("PNG") == true) {
			mExtesion = "png";
		} else {
			mExtesion = "jpg";
		}
		String path = String.format("%s/%s", absolutePath, fileName);
		Bitmap bitmap = BitmapUtil.openBitmap(path);
		mDrawable = new BitmapDrawable(getResources(), bitmap);
		iv_compress.setImageDrawable(mDrawable);
		//這裡也可以直接使用setImageBitmap
		//iv_compress.setImageBitmap(bitmap);
		//但是setBackground就只能用Drawable,不能用Bitmap了
		//iv_compress.setBackground(mDrawable);
	}

	@Override
	public boolean isFileValid(String absolutePath, String fileName,
			Map map_param) {
		return true;
	}

}


調整大小

調整圖片大小可使用createScaledBitmap方法,該函數保留了圖片的全貌,只做尺寸的縮小和放大。下面是調整圖片大小的代碼例子:
import java.util.Locale;
import java.util.Map;

import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.exmimage.dialog.FileSaveFragment;
import com.example.exmimage.dialog.FileSelectFragment;
import com.example.exmimage.dialog.FileSaveFragment.FileSaveCallbacks;
import com.example.exmimage.dialog.FileSelectFragment.FileSelectCallbacks;
import com.example.exmimage.util.BitmapUtil;

public class ResizeActivity extends Activity implements OnClickListener,
		OnLongClickListener, FileSelectCallbacks, FileSaveCallbacks {

	private ImageView iv_resize;
	private TextView tv_width, tv_height;
	private EditText et_width, et_height;
	private int mWidth, mHeight;
	private int mNewWidth, mNewHeight;
	private Bitmap mBitmap = null;
	private String mExtesion;
	private int mQuality = 100;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_resize);

		Button btn_open_resize = (Button) findViewById(R.id.btn_open_resize);
		Button btn_save_resize = (Button) findViewById(R.id.btn_save_resize);
		btn_open_resize.setOnClickListener(this);
		btn_save_resize.setOnClickListener(this);

		iv_resize = (ImageView) findViewById(R.id.iv_resize);
		tv_width = (TextView) findViewById(R.id.tv_width);
		tv_height = (TextView) findViewById(R.id.tv_height);
		et_width = (EditText) findViewById(R.id.et_width);
		et_width.setOnLongClickListener(this);
		et_width.addTextChangedListener(mWidthWatcher);
		et_height = (EditText) findViewById(R.id.et_height);
		et_height.setOnLongClickListener(this);
		et_height.addTextChangedListener(mHeightWatcher);
	}

	@Override
	public boolean onLongClick(View v) {
		if (v.getId() == R.id.et_width) {
			et_width.setText("");
		} else if (v.getId() == R.id.et_height) {
			et_height.setText("");
		}
		return true;
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == R.id.btn_open_resize) {
			FileSelectFragment.show(this, new String[] { "jpg", "png" }, null);
		} else if (v.getId() == R.id.btn_save_resize) {
			if (mBitmap == null) {
				Toast.makeText(this, "請先打開圖片文件", Toast.LENGTH_LONG).show();
				return;
			}
			mNewWidth = Integer.parseInt(et_width.getText().toString());
			mNewHeight = Integer.parseInt(et_height.getText().toString());
			if (mNewWidth<=0 || mNewHeight<=0) {
				Toast.makeText(this, "新圖片的寬和高必須大於0", Toast.LENGTH_LONG)
						.show();
				return;
			}
			FileSaveFragment.show(this, mExtesion);
		}
	}

	@Override
	public boolean onCanSave(String absolutePath, String fileName) {
		return true;
	}

	@Override
	public void onConfirmSave(String absolutePath, String fileName) {
		String path = String.format("%s/%s", absolutePath, fileName);
		Bitmap bitmap = Bitmap.createScaledBitmap(mBitmap, mNewWidth, mNewHeight, false);
		BitmapUtil.saveBitmap(path, bitmap, mExtesion, mQuality);
		Toast.makeText(this, "成功保存圖片文件:" + path, Toast.LENGTH_LONG).show();
	}

	@Override
	public void onConfirmSelect(String absolutePath, String fileName,
			Map map_param) {
		String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
		if (extension.toUpperCase(Locale.getDefault()).equals("PNG") == true) {
			mExtesion = "png";
		} else {
			mExtesion = "jpg";
		}
		String path = String.format("%s/%s", absolutePath, fileName);
		mBitmap = BitmapUtil.openBitmap(path);
		iv_resize.setImageBitmap(mBitmap);
		
		mWidth = mBitmap.getWidth();
		mHeight = mBitmap.getHeight();
		tv_width.setText(""+mWidth);
		tv_height.setText(""+mHeight);
	}

	@Override
	public boolean isFileValid(String absolutePath, String fileName,
			Map map_param) {
		return true;
	}
	
	private TextWatcher mWidthWatcher = new TextWatcher() {
		
		@Override
		public void beforeTextChanged(CharSequence s, int start, int count, int after) {
		}

		@Override
		public void onTextChanged(CharSequence s, int start, int before, int count) {
		}

		@Override
		public void afterTextChanged(Editable s) {
			float newWidth = Integer.parseInt(s.toString());
			et_height.removeTextChangedListener(mHeightWatcher);
			et_height.setText(""+(int)(newWidth/mWidth*mHeight));
			et_height.addTextChangedListener(mHeightWatcher);
		}

	};

	private TextWatcher mHeightWatcher = new TextWatcher() {
		
		@Override
		public void beforeTextChanged(CharSequence s, int start, int count, int after) {
		}

		@Override
		public void onTextChanged(CharSequence s, int start, int before, int count) {
		}

		@Override
		public void afterTextChanged(Editable s) {
			float newHeight = Integer.parseInt(s.toString());
			et_width.removeTextChangedListener(mWidthWatcher);
			et_width.setText(""+(int)(newHeight/mHeight*mWidth));
			et_width.addTextChangedListener(mWidthWatcher);
		}

	};

}


圖片裁剪

裁剪圖片有兩種方法,一種是調用系統服務com.android.camera.action.CROP,該方法編碼簡單,但功能有限;另一種是自己寫個裁剪算法,編碼麻煩些,不過可定制實現復雜的功能。


下面是調用系統服務實現圖片裁剪的代碼例子(只列出了關鍵代碼):
	@Override
	public void onClick(View v) {
		if (v.getId() == R.id.btn_open_system) {
			FileSelectFragment.show(this, new String[] { "jpg", "png" }, null);
		} else if (v.getId() == R.id.btn_save_system) {
			if (mNewBitmap == null) {
				Toast.makeText(this, "請先打開並裁剪圖片文件", Toast.LENGTH_LONG).show();
				return;
			}
			FileSaveFragment.show(this, mExtesion);
		} else if (v.getId() == R.id.btn_cut_system) {
            Intent intent = new Intent("com.android.camera.action.CROP");
            intent.setDataAndType(Uri.parse("file://"+mOldFilePath), "image/*");
            // 設置裁剪
            intent.putExtra("crop", "true");
            // aspectX aspectY 是寬高的比例
            intent.putExtra("aspectX", 1);
            intent.putExtra("aspectY", 1);
            // outputX outputY 是裁剪圖片寬高
            intent.putExtra("outputX", 300);
            intent.putExtra("outputY", 300);
            intent.putExtra("return-data", false);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse("file://"+mNewFilePath));
            intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
            startActivityForResult(intent, RESULT_REQUEST_CODE);
		}
	}
	
	@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK) {
            if (requestCode == RESULT_REQUEST_CODE) {
        		mNewBitmap = BitmapUtil.openBitmap(mNewFilePath);
        		iv_system_new.setImageBitmap(mNewBitmap);
            	//iv_system_new.setImageDrawable(Drawable.createFromPath(mNewFilePath));
            }
        }
    }


自己編碼實現裁剪圖片的話,一般是把圖片分為兩塊區域,一塊是裁剪的內部區域,需高亮顯示;另一塊位於裁剪區域外部,需陰影顯示。這個編碼似乎沒有捷徑,博主想到的辦法是采用FrameLayout布局,內部放三個子視圖,分別是:
1、原圖片的ImageView;
2、陰影部分的View,裁剪開始時顯示,裁剪結束後隱藏;
3、裁剪區域的ImageView,裁剪開始時顯示,裁剪結束後隱藏;


這裡實現的難點在於裁剪區域的ImageView,得基於ImageView自定義一種視圖CropImageView。該視圖的編碼思路大致有三部分內容,首先,我們要按照設定的區域從原圖片中截取一塊位圖出來,該功能可調用Bitmap的createBitmap方法來實現。其次,在手勢按下時,根據當前按下的位置,判斷接下來的裁剪動作,是拖動整個裁剪區域,還是移動某條邊,還是移動某個角,這裡一共要做十個判斷(四條邊、四個角、按在區域內部要拖動、按在區域外部不處理)。最後,重寫onTouchEvent方法,在按下動作ACTION_DOWN時初始化觸摸條件,在移動操作ACTION_MOVE時,根據裁剪動作刷新圖片顯示。


下面是自定義裁剪視圖的效果截圖:
\


下面是CropImageView的實現代碼例子:
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;

public class CropImageView extends ImageView {

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

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

	public CropImageView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	private Bitmap mOrigBitmap = null;
	private Bitmap mCropBitmap = null;
	private Rect mRect = new Rect(0,0,0,0);
	
	public void setOrigBitmap(Bitmap orig) {
		mOrigBitmap = orig;
	}
	
	public Bitmap getOrigBitmap() {
		return mOrigBitmap;
	}

	public Bitmap getCropBitmap() {
		return mCropBitmap;
	}
	
	public boolean setBitmapRect(Rect rect) {
		if (mOrigBitmap == null) {
			return false;
		}
		if (rect.left<0 || rect.left>mOrigBitmap.getWidth()) {
			return false;
		}
		if (rect.top<0 || rect.top>mOrigBitmap.getHeight()) {
			return false;
		}
		if (rect.right<=0 || rect.left+rect.right>mOrigBitmap.getWidth()) {
			return false;
		}
		if (rect.bottom<=0 || rect.top+rect.bottom>mOrigBitmap.getHeight()) {
			return false;
		}
		mRect = rect;
		setPadding(mRect.left, mRect.top, 0, 0);
		mCropBitmap = Bitmap.createBitmap(mOrigBitmap, 
				mRect.left, mRect.top, mRect.right, mRect.bottom);
		setImageBitmap(mCropBitmap);
		postInvalidate();
		return true;
	}

	public Rect getBitmapRect() {
		return mRect;
	}
	
	@SuppressLint("ClickableViewAccessibility")
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mOriginX = event.getX();
			mOriginY = event.getY();
			mOriginRect = mRect;
			mDragMode = getDragMode(mOriginX, mOriginY);
			break;
		case MotionEvent.ACTION_MOVE:
			int offsetX = (int) (event.getX()-mOriginX);
			int offsetY = (int) (event.getY()-mOriginY);
			Rect rect = null;
			if (mDragMode == DRAG_NONE) {
				return true;
			} else if (mDragMode == DRAG_WHOLE) {
				rect = new Rect(mOriginRect.left+offsetX, mOriginRect.top+offsetY, mOriginRect.right, mOriginRect.bottom);
			} else if (mDragMode == DRAG_LEFT) {
				rect = new Rect(mOriginRect.left+offsetX, mOriginRect.top, mOriginRect.right-offsetX, mOriginRect.bottom);
			} else if (mDragMode == DRAG_RIGHT) {
				rect = new Rect(mOriginRect.left, mOriginRect.top, mOriginRect.right+offsetX, mOriginRect.bottom);
			} else if (mDragMode == DRAG_TOP) {
				rect = new Rect(mOriginRect.left, mOriginRect.top+offsetY, mOriginRect.right, mOriginRect.bottom-offsetY);
			} else if (mDragMode == DRAG_BOTTOM) {
				rect = new Rect(mOriginRect.left, mOriginRect.top, mOriginRect.right, mOriginRect.bottom+offsetY);
			} else if (mDragMode == DRAG_LEFT_TOP) {
				rect = new Rect(mOriginRect.left+offsetX, mOriginRect.top+offsetY, mOriginRect.right-offsetX, mOriginRect.bottom-offsetY);
			} else if (mDragMode == DRAG_RIGHT_TOP) {
				rect = new Rect(mOriginRect.left, mOriginRect.top+offsetY, mOriginRect.right+offsetX, mOriginRect.bottom-offsetY);
			} else if (mDragMode == DRAG_LEFT_BOTTOM) {
				rect = new Rect(mOriginRect.left+offsetX, mOriginRect.top, mOriginRect.right-offsetX, mOriginRect.bottom+offsetY);
			} else if (mDragMode == DRAG_RIGHT_BOTTOM) {
				rect = new Rect(mOriginRect.left, mOriginRect.top, mOriginRect.right+offsetX, mOriginRect.bottom+offsetY);
			}
			setBitmapRect(rect);
			break;
		case MotionEvent.ACTION_UP:
		case MotionEvent.ACTION_CANCEL:
			break;
		default:
			break;
		}
		return true;
	}

	private int DRAG_NONE = 0;
	private int DRAG_WHOLE = 1;
	private int DRAG_LEFT = 2;
	private int DRAG_RIGHT = 3;
	private int DRAG_TOP = 4;
	private int DRAG_BOTTOM = 5;
	private int DRAG_LEFT_TOP = 6;
	private int DRAG_RIGHT_TOP = 7;
	private int DRAG_LEFT_BOTTOM = 8;
	private int DRAG_RIGHT_BOTTOM = 9;
	
	private int mDragMode = DRAG_NONE;
	private int mInterval = 10;
	private float mOriginX, mOriginY;
	private Rect mOriginRect;
	
	private int getDragMode(float f, float g) {
		int left = mRect.left;
		int top = mRect.top;
		int right = mRect.left + mRect.right;
		int bottom = mRect.top + mRect.bottom;
		if (Math.abs(f-left)<=mInterval && Math.abs(g-top)<=mInterval) {
			return DRAG_LEFT_TOP;
		} else if (Math.abs(f-right)<=mInterval && Math.abs(g-top)<=mInterval) {
			return DRAG_RIGHT_TOP;
		} else if (Math.abs(f-left)<=mInterval && Math.abs(g-bottom)<=mInterval) {
			return DRAG_LEFT_BOTTOM;
		} else if (Math.abs(f-right)<=mInterval && Math.abs(g-bottom)<=mInterval) {
			return DRAG_RIGHT_BOTTOM;
		} else if (Math.abs(f-left)<=mInterval && g>top+mInterval && gtop+mInterval && gtop+mInterval && gleft+mInterval && fleft+mInterval && fleft+mInterval && ftop+mInterval && g

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved