1. 程式人生 > >Android開發:相簿讀取、拍照、圖片裁剪和圖片上傳伺服器等功能的實現

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開發如何在選單中呼叫控制元件(如ButtonTextView……)

當我們在類內定義控制元件的全域性變數時,如Button……,只能在onCreate()中初始化,這樣的控制元件變數引用在選單中不好引用,會報錯。 如果想在選單中呼叫控制元件,可以在選單中重新定義控制元件

Android開發最全面最易懂的Android螢幕適配解決方案

前言 Android的螢幕適配一直以來都在折磨著我們Android開發者,本文將結合: 給你帶來一種全新、全面而邏輯清晰的Android螢幕適配思路,只要你認真閱讀,保證你能解決Android的螢幕適配問題! 目錄 定義 使得某一元

Android開發最全面最易懂的Webview詳解

現在很多App裡都內建了Web網頁(Hyprid App),比如說很多電商平臺,淘寶、京東、聚划算等等,如下圖 京東首頁.jpg 那麼這種該如何實現呢?其實這是Android裡一個叫WebView的元件實現的。今天我將全面介紹WebView的常用用法。 目錄 文章目錄 1. 簡介

Android開發JSON簡介及最全面解析方法(GsonAS自帶org.jsonJackson解析)

目錄 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開發中使用到的 JSONArrayJSONObject

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開發環境變數的配置(javasdkndkgradle)

很多文章說了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開發之獲取網路型別(WIFI2G3G4G)運營商名稱

  在Android開發中,常常使用到網路,可能需要針對不同的網路(WIFI或者流量),需要做不同的處理,那麼怎麼獲取當前網路呢?為此寫了個工具類,以後需要使用,直接拿來用就行(就喜歡拿來主義,哈哈)。 package com.wangguifa.phon