Android開發:相簿讀取、拍照、圖片裁剪和圖片上傳伺服器等功能的實現
修改日誌
2016.05.12
之前的程式存在兩個問題:
1)從相簿選擇的圖片如果比較大,會失敗;
2)無法拍照上傳照片。
修改了這兩個bug,之前的程式碼已經被覆蓋掉了,留著太誤人子弟了。同時修改了一下標題和文章的文字描述
拍照示意
相簿示意
一. 整體功能描述
整理了一下主要有以下幾點功能:
1)獲得相簿圖片
2)通過拍照獲得圖片
3)裁剪圖片
4)將圖片上傳至伺服器
5)從伺服器獲得圖片
二. 功能實現
1.1 獲得圖片
(1)通常情況下,有以下兩種方式:
**從相簿中選擇圖片
這種方式原理比較簡單,就是從SDK中獲得照片,轉成位元組再生產Bitmap物件用於顯示即可。**拍照獲得圖片
拍照獲取的圖片原理就是先拍照儲存,然後再讀取,就和從相簿中選擇圖片的原理一樣了。
(2)實現:
關於圖片的選擇和處理,推薦一葉飄舟的文章。
- 首先是點選頭像彈出一個dialog供選擇讀入圖片的方式:
private CharSequence []its = {"拍照","從相簿選擇"};
public static final int TAKE_PHOTO = 1;//拍照
public static final int CROP_PHOTO = 2;//裁剪
public static final int SELECT_PIC = 0;//從相簿選擇
private Uri imageUri; //圖片路徑
private String filename; //圖片名稱
//上傳頭像
headImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(ModiUserInfoActivity.this)
.setTitle("更換頭像")
.setItems(its, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
//最好自己封裝一下,方便複用
switch (which)
{
case 0://拍照
//圖片名稱 時間命名
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
Date date = new Date(System.currentTimeMillis());
filename = format.format(date);
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File outputImage = new File(path,filename+".jpg");
try {
if(outputImage.exists())
{
outputImage.delete();
}
outputImage.createNewFile();
} catch(IOException e) {
e.printStackTrace();
}
//將File物件轉換為Uri並啟動照相程式
imageUri = Uri.fromFile(outputImage);
Intent tTntent = new Intent("android.media.action.IMAGE_CAPTURE"); //照相
tTntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); //指定圖片輸出地址
startActivityForResult(tTntent,TAKE_PHOTO); //啟動照相
break;
case 1://從相簿選擇
intent.setAction(Intent.ACTION_GET_CONTENT);//ACTION_OPEN_DOCUMENT
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
intent.putExtra("return-data", true);
intent.putExtra("crop", "true");
//設定寬高比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
//設定裁剪圖片寬高、
intent.putExtra("outputX", 450);
intent.putExtra("outputY", 450);
startActivityForResult(intent,SELECT_PIC);
break;
}
}
})
.create()
.show();
}
});
}
- 然後就是對圖片資料進行處理了
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK)
return;
switch (requestCode)
{
case SELECT_PIC://相簿
photo = data.getParcelableExtra("data");
if(photo!=null)
{
headImage = ImageDeal.toRoundBitmap(photo);//裁剪成圓形
//上傳到伺服器...
UserController.updateHeadImage(userId, headImage, handler);
photo.recycle();
}
break;
case SELECT_CAMERA://相機
try {
Intent intent = new Intent("com.android.camera.action.CROP"); //剪裁
intent.setDataAndType(imageUri, "image/*");
intent.putExtra("scale", true);
//設定寬高比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
//設定裁剪圖片寬高
intent.putExtra("outputX", 450);
intent.putExtra("outputY", 450);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
Toast.makeText(ModiUserInfoActivity.this, "剪裁圖片", Toast.LENGTH_SHORT).show();
//廣播重新整理相簿
Intent intentBc = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intentBc.setData(imageUri);
this.sendBroadcast(intentBc);
startActivityForResult(intent, CROP_PHOTO); //設定裁剪引數顯示圖片至ImageView
break;
} catch (Exception e) {
e.printStackTrace();
}
break;
case CROP_PHOTO:
try {
//圖片解析成Bitmap物件
Bitmap bitmap = BitmapFactory.decodeStream(
getContentResolver().openInputStream(photoFile));
headImage = ImageDeal.toRoundBitmap(bitmap);
UserController.updateHeadImage(userId, headImage, handler);
} catch(FileNotFoundException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
ok,按照上面的程式碼,從相簿選擇照片和拍照 裁剪就沒有問題了,至於對圖片的其他一些處理,可以參考一葉飄舟的博文。
1.2上傳到伺服器並儲存
查閱了很多資料,使用的方式要麼都是那種上傳檔案那類看起來就特別複雜的方式,看的我真的頭大了。後來找了好幾個版本自己都沒能成功實現把1.1中獲取的圖片上傳到伺服器。後來回想了一下http請求不就是傳送的位元組嘛,那我把圖片轉成位元組不就能傳過去了麼,另一個問題又來了,有時候我們並不是單單傳送一張圖片過去,可能還有其他資訊,比如一個表單或者一個id之類的,總之要傳送的資料還有不僅僅是圖片。之前傳送資料都是通過json或者map轉成String再轉成byte[]實現的,那麼現在傳送的是圖片+其他資料,思路便是:圖片->String,然後和其他資料一起打包成json->String->byte[]->Bitmap。
- 客戶端
現在接著寫1.1中程式碼使用到的UserController.updateHeadImage(userId, headImage, handler)。
/**
* 儲存頭像
* @param userId
* @param handler
* @param image
*/
public static void updateHeadImage(final String userId,final Bitmap image, final Handler handler)
{
new Thread()
{
@Override
public void run()
{
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("userId",userId);
//將Bitmap轉成String,其實這是一個加密過程。後面會有Common.Bitmap2String()的程式碼。
jsonObject.put("userImageContent", Common.Bitmap2String(image));
String content = String.valueOf(jsonObject);
/**
* 請求地址
*/
String url = ConfigModel.getServerURL()+"user/updateImage";
String result = Common.httpPost(url,content);//post請求伺服器:資料content,請求路徑url。這個函式也是自己寫在Common類裡,其實就是稍微封裝了一下post請求的過程,方便複用。
Log.i("result",result);
/**
* 伺服器返回結果
* 繼續幹什麼事情....
*/
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putString("result",result);
message.setData(bundle);
message.what = R.id.save_user_image_result;
handler.sendMessage(message);
}catch (Exception e){}
}
}.start();
}
- 伺服器端
客戶端將圖片加密成了String,那麼服務端就需要解碼就可以獲得圖片。
//第一步,將資料流轉String,自己封裝成了一個read函式,方便複用;
String streamIn = ReadStream.read(new BufferedInputStream(request.getInputStream()));
//JSONObject object = JSONObject.fromObject(streamIn).getJSONObject("user");
JSONObject object = JSONObject.fromObject(streamIn);//String轉JSON
String userId = object.getString("userId");
String userImageContent = object.getString("userImageContent");//獲得影象的資料
//..其他步驟省略
//..比如判斷是否是新影象,比如生成影象ID imgId = Tool.getUUID();
//第二步將影象資料String轉成Bitmap
byte[] bitmapArray= Base64.decode(imageContent);
try
{
File imageFile = new File(headImagePath);
if(!imageFile.exists())
imageFile.mkdirs();
//建立輸出流
FileOutputStream outStream = new FileOutputStream(imageFile.getPath()+"\\"+imgId+".png");
//寫入資料
outStream.write(bitmapArray);
//關閉輸出流
outStream.close();
}
catch(IOException e)
{
System.out.println(e);
}
由此就完美的將客戶端的影象和資料都上傳到伺服器上了。下面部分就是從伺服器上獲得圖片和資料再返回給客戶端,有了前面部分的思路,那這部分就很容易實現,反其道而行之就可以了。
1.3從伺服器中獲得圖片並顯示
- 伺服器端
伺服器端要做的事情就是將Bitmap轉成String,然後和其他資料打包成json,返回給客戶端。
//讀取圖片,轉成String
public static String readImage(String imgId)
{
//byte []data = imageContent.getBytes();
File file = new File(headImagePath+imgId+".png");
try {
FileInputStream inputStream = new FileInputStream(file);
byte[] bitmapArray = new byte[(int) file.length()];
inputStream.read(bitmapArray);
inputStream.close();
return Base64.encode(bitmapArray);
} catch(Exception e)
{
return "";
}
}
//*****************************************
//程式碼片.....和上面的程式碼不在同一個java檔案中
//*****************************************
/**
* 查詢一個user
*
* @param userId
* 主鍵
* @return user
*/
@RequestMapping(value = "/user/get/{id}")
@ResponseBody
public JSONObject get(@PathVariable("id") String userId,
HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", "*");
User user = userDAO.getUser(userId);
JSONObject jsonObject = new JSONObject();
jsonObject.put("user", user);
if(user.getUserImage()!=null&&!"".equals(user.getUserImage()))
{
jsonObject.put("userImageContent", Tool.readImage(user.getUserImage()));
}
return jsonObject;
}
- 客戶端
這部分我就不貼程式碼了,因為自己把很多功能都封裝了,要貼出來有點麻煩,總之此時的圖片資料已經轉成了String,只需要在顯示的時候,再轉成Bitmap。String->Bitmap的實現請參考我的Common類。
1.4輔助工具
/**
* 影象轉位元組
* @param bm
* @return
*/
public static byte[] Bitmap2Bytes(Bitmap bm) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
}
/**
* 影象轉String
* @param bitmap
* @return
*/
public static String Bitmap2String(Bitmap bitmap)
{
return Base64.encodeToString(Bitmap2Bytes(bitmap), Base64.DEFAULT);
}
/**
* string轉成bitmap
*
* @param st
*/
public static Bitmap String2Bitmap(String st)
{
Bitmap bitmap = null;
try
{
byte[] bitmapArray;
bitmapArray = Base64.decode(st, Base64.DEFAULT);
bitmap = BitmapFactory.decodeByteArray(bitmapArray, 0, bitmapArray.length);
return bitmap;
}
catch (Exception e)
{
return null;
}
}
/**
* 把bitmap轉成圓形
* */
public static Bitmap toRoundBitmap(Bitmap bitmap){
int width=bitmap.getWidth();
int height=bitmap.getHeight();
int r=0;
//取最短邊做邊長
if(width<height){
r=width;
}else{
r=height;
}
//構建一個bitmap
Bitmap backgroundBm= Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
//new一個Canvas,在backgroundBmp上畫圖
Canvas canvas=new Canvas(backgroundBm);
Paint p=new Paint();
//設定邊緣光滑,去掉鋸齒
p.setAntiAlias(true);
RectF rect=new RectF(0, 0, r, r);
//通過制定的rect畫一個圓角矩形,當圓角X軸方向的半徑等於Y軸方向的半徑時,
//且都等於r/2時,畫出來的圓角矩形就是圓形
canvas.drawRoundRect(rect, r/2, r/2, p);
//設定當兩個圖形相交時的模式,SRC_IN為取SRC圖形相交的部分,多餘的將被去掉
p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//canvas將bitmap畫在backgroundBmp上
canvas.drawBitmap(bitmap, null, rect, p);
return backgroundBm;
}
相關推薦
Android開發:仿微信和QQ空間發說說相簿讀取、拍照、圖片裁剪和圖片上傳伺服器等功能的實現
第一步:新增依賴包: dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.2.1' compile project('
Android開發:相簿讀取、拍照、圖片裁剪和圖片上傳伺服器等功能的實現
修改日誌 2016.05.12 之前的程式存在兩個問題: 1)從相簿選擇的圖片如果比較大,會失敗; 2)無法拍照上傳照片。 修改了這兩個bug,之前的程式碼已經被覆蓋掉了,留著太誤人子弟了。同時修改了一下標題和文章的文字描述 拍照示意 相簿示
Android開發:解決三星裝置拍照後獲取的圖片被旋轉的問題
呼叫Android系統拍照功能後,三星手機拍攝後的照片被旋轉了90度,橫著拍變成豎的,豎著拍變成橫的。。。測試其它品牌的手機是正常的,就三星出現這個問題。 解決方法: 1、首先在呼叫拍照方法時,儲存拍照後的相片原圖,得到原圖路徑,(PhotoBitmapUtils是我
Android開發 :呼叫系統相機拍照儲存照片並顯示在當前介面
主要思路:呼叫照相機拍照,然後為拍得的照片在SD卡新開一個儲存照片的檔案,然後在onActivityResult中進行照片顯示 1、因為要呼叫照相機和SD卡所以需要在manifest.xml中新增以下許可權: <uses-permission android:na
Android開發本地及網路Mp3音樂播放器(十五)網路音樂及歌詞下載功能實現
實現功能: 實現網路音樂歌詞下載功能(下載音樂的同時,下載對應歌詞) 下載好的歌詞目前不在播放器內,可以通過檔案瀏覽器檢視。 後續將博文,將實現本地音樂歌詞下載和已下載音樂掃描功能。 因為,沒有自己的伺服器,所以網路音樂所有相關功能(包含搜尋音樂、下載音樂、下載歌詞)均無法
Android開發筆記(一百五十二)H5通過WebView上傳圖片
上一篇文章介紹了WebView與JS之間的資料互動,其實就是把字串傳來傳去,這對文字格式的資訊傳輸來說倒還湊合,倘若要傳輸圖片資訊就不管用了。所以,要想讓h5網頁支援從手機上傳圖片,還得另外想辦法,當然各版本的Android系統也都提供了相應的解決辦法。在Android 4.
Android開發:SharedPreferences 存儲數據、獲取數據
.get 本地 www phone win7 popu ces androi studio Android開發:SharedPreferences 存儲數據、獲取數據 email:[email protected]/* */ 開發環境:w
Android開發:如何在選單中呼叫控制元件(如Button、TextView……)
當我們在類內定義控制元件的全域性變數時,如Button……,只能在onCreate()中初始化,這樣的控制元件變數引用在選單中不好引用,會報錯。 如果想在選單中呼叫控制元件,可以在選單中重新定義控制元件
Android開發:最全面、最易懂的Android螢幕適配解決方案
前言 Android的螢幕適配一直以來都在折磨著我們Android開發者,本文將結合: 給你帶來一種全新、全面而邏輯清晰的Android螢幕適配思路,只要你認真閱讀,保證你能解決Android的螢幕適配問題! 目錄 定義 使得某一元
Android開發:最全面、最易懂的Webview詳解
現在很多App裡都內建了Web網頁(Hyprid App),比如說很多電商平臺,淘寶、京東、聚划算等等,如下圖 京東首頁.jpg 那麼這種該如何實現呢?其實這是Android裡一個叫WebView的元件實現的。今天我將全面介紹WebView的常用用法。 目錄 文章目錄 1. 簡介
Android開發:JSON簡介及最全面解析方法(Gson、AS自帶org.json、Jackson解析)
目錄 JSON簡介&解析方法介紹.png 定義 JavaScript Object Notation,JavaScript的物件表示法,是一種輕量級的文字資料交換格式。 作用 用於資料的標記、儲存和傳輸。 特點 輕量級的文字資料交換格式 獨立於語言和平臺 具有自我描述性 讀寫速度快,解析簡單 語法
Android UsageStats:應用根據啟動次數、啟動時間、應用名稱排序
public each eno you when listen iss 技術 internal Android 7.1.1 developers/samples/android/system/AppUsageStatistics/Application/src/main/j
Python 全棧開發:python三元表達式、遞歸、匿名函數
函數的參數 def 開發 col foo div 規則 尾遞歸 python 三元表達式 基本的語法格式 為真時的結果 if 判定條件 else 為假時的結果 比較兩個數中的大值 並返回 #一般函數的寫法def max2(x,y): if x > y
Android開發優化之的強引用、軟引用、弱引用的使用
本文轉載至:http://www.jianshu.com/p/8488079a939b 引言 早在JDK1.2,Java就把物件的引用分為四種級別,從而使程式能更加靈活的控制物件的生命週期。這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。 但是平時我們的程式碼中似乎很
Android開發中使用到的 JSONArray、JSONObject
JSONArray .java import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import CommonUtil; /** * Json物件陣列
libusb開發:bulk/interrupt資料傳輸、hotplug熱插拔
工作之餘花費較多精力寫的基於libusb的應用程式,包含了libusb大部分功能。程式碼放在了github上,有需要可以下載做參考。 程式碼已在Ubuntu上編譯並測試通過,測試時需要將一個usb de
Android開發之裁切(拍照+相簿)影象並設定頭像小結
先看效果: 再貼程式碼: 自定義選擇照片底部彈出對話方塊佈局: <?xml version="1.0" encoding="utf-8"?> <Re
Mac Android開發環境變數的配置(java、sdk、ndk、gradle)
很多文章說了mac下怎樣配置環境變數的問題,但大都很雜,在此總結一下,目的是使Android開發者看一篇部落格就可以配置好AndroidStudio開發下的環境變數。 樓主的JDK版本是:1.8.0_40; AndroidStudio版本:2.2;
微信瀏覽器內相容android iOS調取手機攝像頭進行拍照、打水印、壓縮、預覽
實現這些功能使用瞭如下外掛: html部分 //調起攝像頭按鈕 <input type="file" id="file" accept="image/*" capture="camera"> //預覽圖片 <div id="file
Android開發之獲取網路型別(WIFI、2G、3G、4G)和運營商名稱
在Android開發中,常常使用到網路,可能需要針對不同的網路(WIFI或者流量),需要做不同的處理,那麼怎麼獲取當前網路呢?為此寫了個工具類,以後需要使用,直接拿來用就行(就喜歡拿來主義,哈哈)。 package com.wangguifa.phon