1. 程式人生 > >Android呼叫相機拍照並新增水印

Android呼叫相機拍照並新增水印

最近專案提出新需求,要求在拍照後在圖片上新增使用者資訊的水印,so,樓主重新整理封裝了一個簡單的拍照+水印的工具類,需求不同,僅供參考。檔案操作及許可權做了適配,支援androidN及以上版本,放心使用。

效果演示:


如何呼叫相機這裡就不介紹了,因為小夥伴們基本都使用過,主要介紹下實現水印的方式,實現水印效果也很簡單,利用paint及canvas操作bitmap在對應位置進行繪製文字即可,首先定義畫筆,設定水印顏色、大小及文字:

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setTextSize(size);
Rect bounds = new Rect(); paint.getTextBounds(text, 0, text.length(), bounds);

定義好畫筆後新建canvas對bitmap進行繪製,這裡應該注意的是在Android程式碼裡是不允許直接修改Bitmap資原始檔,如果不copy一份的話會拋異常

Caused by: java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor

繪製程式碼如下:

Bitmap.Config bitmapConfig = bitmap.getConfig();
paint.setDither(true); // 獲取跟清晰的影象取樣 paint.setFilterBitmap(true);// 過濾一些 if (bitmapConfig == null) { bitmapConfig = Bitmap.Config.ARGB_8888; } bitmap = bitmap.copy(bitmapConfig, true); Canvas canvas = new Canvas(bitmap);canvas.drawText(text, paddingLeft, paddingTop, paint);

我們看下drawText的原始碼

/**
 * Draw the text, with origin at (x,y), using the specified paint. The
* origin is interpreted based on the Align setting in the paint. * * @param text The text to be drawn * @param x The x-coordinate of the origin of the text being drawn * @param y The y-coordinate of the baseline of the text being drawn * @param paint The paint used for the text (e.g. color, size, style) */ public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface); }

第一個和第四個引數顯而易見,第二個和第三個看註釋也能看懂,當然是對應的座標軸x軸和y軸了,但是這個座標軸到底原點在哪呢,樓主自己做了一張簡單的圖片供小夥伴參考,如圖:


座標原點為手機左上角,橫向向右為X軸正極,縱向向下為Y軸正極,這樣我們可以計算left及top來設定水印的不同位置了,樓主在util中封裝了幾個常用的位置,小夥伴可以檢視demo中的程式碼。

繪製文字問題解決了,但是又一個新問題出現了,那就是如何實現多段落顯示及段落間的換行,我們都知道每部手機的相機畫素是不一樣的,如果是把textSize寫死的話,在不同解析度的圖片上顯示效果很差,所以我定義了一個itemCount,每行顯示的文字數,通過itemCount及圖片寬度計算出textSize,比如一部手機拍出的圖片寬為1080畫素,itemCount為20,那麼文字大小為54px,根據textSize計算總文字行數並分段,把每段文字放在list中,最後遍歷List繪製水印,參考程式碼如下:

/**
 * 繪製水印
 *
 * @param context
* @param bitmap
* @param waterMaskParam
* @return
*/
private static Bitmap drawTxt(Context context, Bitmap bitmap, WaterMaskParam waterMaskParam) {
    int maxHeight = 0;  //計算總行數
List<List<String>> msg = new ArrayList<>(); //文字
for (String str : waterMaskParam.txt) {
        int count = str.length() / waterMaskParam.itemCount;
        if (count == 0) {
            maxHeight++;
List<String> list = new ArrayList<>();
list.add(str);
msg.add(list);
} else {
            if (str.length() % waterMaskParam.itemCount != 0) {
                count++;
}
            List<String> list = new ArrayList<>();
            for (int i = 0; i < count; i++) {
                String s = str.substring(i * waterMaskParam.itemCount,
i == count - 1 ? str.length() : (i + 1) * waterMaskParam.itemCount);
list.add(s);
}
            msg.add(list);
maxHeight += count;
}
    }
    int txtSize = bitmap.getWidth() / waterMaskParam.itemCount;
    int index = msg.size() - 1;
bitmap = checkBackground(waterMaskParam.location, bitmap, msg.size() * txtSize * 2.0f);
    for (List<String> strings : msg) {
        for (int i = 0; i < strings.size(); i++) {
            bitmap = checkType(context, bitmap, strings.get(i), txtSize,
waterMaskParam, txtSize * (maxHeight--) + index * txtSize / 2);
}
        index--;
}
    return bitmap;
}
我在最後繪製水印時,list中嵌套了一個list,外迴圈用來處理多段落的換行問題,內迴圈用來處理每個段落中的換行問題,所以我在呼叫繪製文字外部類傳值時用的是list而不是string,及list中每個元素為一個段落,model如下:
public static class WaterMaskParam {
    public List<String> txt = new ArrayList<>(); //水印文字
public int itemCount = DefWaterMaskParam.ITEM_COUNT; //每行的文字數
public int txtColor = DefWaterMaskParam.TEXT_COLOR; //文字顏色
public int location = DefWaterMaskParam.Location.left_bottom; //水印位置
public WaterMaskParam() {
    }

    public WaterMaskParam(List<String> txt) {
        this.txt = txt;
}

    public WaterMaskParam(List<String> txt, int itemCount, int txtColor) {
        this.txt = txt;
        this.itemCount = itemCount;
        this.txtColor = txtColor;
}
}
為了優化水印,我在水印背景處又加了印象效果,模擬器拍攝的圖片,不美觀見諒


背景色的實現方式與水印繪製方式大同小異:

/**
 * 繪製背景色
 *
 * @param bitmap
* @param maxHeight 水印最大高度
 * @return
*/
private static Bitmap drawBackground(Bitmap bitmap, float maxHeight) {
    Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Paint paint_b = new Paint();
paint_b.setDither(true);
paint_b.setFilterBitmap(true);
paint_b.setColor(Color.BLACK);
paint_b.setDither(true);
paint_b.setFilterBitmap(true);
paint_b.setAlpha(100);
canvas.drawBitmap(bitmap, 0, 0, null);
canvas.drawRect(0, newBitmap.getHeight() - maxHeight, newBitmap.getWidth(),
newBitmap.getHeight(), paint_b);
    return newBitmap;
}

我封裝了一個helper類用來簡化呼叫:

public class WaterMaskHelper {

    private Context context;
    private PhotoListener photoListener;
    private WaterMask.WaterMaskListener waterMarkListener;
    public WaterMaskHelper(Context context, PhotoListener photoListener, WaterMask.WaterMaskListener waterMarkListener) {
        this.context = context;
        this.photoListener = photoListener;
        this.waterMarkListener = waterMarkListener;
}

    public void startCapture() {
        context.startActivity(new Intent(context, PhotoCaptureActivity.class));
PhotoCaptureActivity.setWaterListener(waterMarkListener);
PhotoCaptureActivity.setPhotoListener(photoListener);
}
}

在activity中例項helper並實現介面:

//初始化水印工具
waterMaskHelper = new WaterMaskHelper(this, this, this);

呼叫startCapture呼叫相機拍照並新增水印:

waterMaskHelper.startCapture();

暴露的介面有兩個,都是在相機拍照後呼叫,選擇照片:

//選擇照片的uri,預設為下標1的元素
void onChoose(ArrayList<String> photos);
新增水印:
//拍照後呼叫,設定水印的基本引數
WaterMaskParam onDraw();

具體實現,注:txt為空或param為空時不繪製水印

@Override
public WaterMask.WaterMaskParam onDraw() {
    WaterMask.WaterMaskParam param = new WaterMask.WaterMaskParam();
param.txt.add("我是一個小標題");
param.txt.add(binding.edt.getText().toString().trim());
param.location = maskLocation;
param.itemCount = 30;
    return param;
}

@Override
public void onChoose(ArrayList<String> photos) {
    uris = photos;
Glide.with(MainActivity.this).load(photos.get(0)).placeholder(R.mipmap.ic_launcher).centerCrop().error(R.mipmap.ic_launcher)
            .crossFade().into(binding.img);
}
相關Demo已上傳至Github,Git地址

Demo中只支援文字繪製,新增圖片的實現方式其實與文字差不多,有興趣的小夥伴可以嘗試。我結合了相機及水印,想單獨呼叫的是可以程式碼分離的哦,耦合度較低。

相關推薦

Android呼叫相機拍照新增水印

最近專案提出新需求,要求在拍照後在圖片上新增使用者資訊的水印,so,樓主重新整理封裝了一個簡單的拍照+水印的工具類,需求不同,僅供參考。檔案操作及許可權做了適配,支援androidN及以上版本,放心使用。 效果演示: 如何呼叫相機這裡就不介紹了,因為小夥伴們基本都使用過

呼叫android系統相機拍照將圖片傳指定路徑獲取相片

public void onClick(View v) { if (v.getId() == R.id.btn_take) { // 調系統相機 if (Environment.getExterna

android呼叫相機拍照返回的照片大小太小,變得模糊

1、使用相機拍照預設情況下呼叫相機的方式: Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(intent, CAMERA_REQUEST_CODE);//CAMERA_REQ

Android呼叫相機拍照後圖片橫向顯示的問題解決

最近在做一個專案的時候出現需要實時拍照然後作為頭像上傳伺服器的一個操作,按照以前的老專案老是出現拍照後對 圖片處理的問題上圖片橫向顯示,這樣切割出來的圖片也就是橫向了,找了很久才解決出來問題的根源,現在記錄下來, 以後遇到這個問題就不會出錯了。 1:一般相機拍完照後正常顯示在螢幕上如圖,

Android呼叫相機拍照,壓縮圖片後儲存SD卡中

最近在搞一個專案,需求是呼叫系統相機拍完照片後儲存本地,再上傳至後臺伺服器,但為了節省流量需要壓縮上傳,將圖片壓縮至100K以內。這個是在特定機器上執行,類似於手持POS機,但是它的相機幾乎沒有優化,對焦慢,而且拍照也不清晰,使用自己的手機呼叫系統相機拍照後圖片

Android--呼叫相機拍照(支援到8.0)

import android.app.Activity; import android.content.ContentValues; import android.content.Intent; import android.net.Uri; import android.o

Android呼叫相機拍照後,壓縮圖片…

1、直接onActivityResult裡 ,從Intent data引數中可以取出拍照後縮圖,基本不需要壓縮的。 2、是設定儲存路徑,然後拍完之後直接讀取該路徑的圖片: Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Str

解決Android 呼叫相機拍照後得到的照片不清晰問題

最近在做一個需要上傳拍照的APP,上傳的時候因為是小圖片,但是需求提了要點選可預覽大圖,一看大圖,被嚇到了,根本看不清楚,大圖中的具體內容。原因是通過MediaStore.ACTION_IMAGE_CAPTURE方式直接去呼叫相機,在onactivityr

關於Android呼叫相機拍照,完成後無法返回

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); String path = Environment.getExternalStorageDirectory().getAbsolutePath()

android呼叫攝像頭拍照顯示

1. 得到一個button並新增事件,先建立一個檔案,然後用Intent通過 “android.media.action.IMAGE_CAPTURE”,呼叫攝像頭。 File outputImage = new File(Environment.getExternalSto

Android 呼叫本地相機拍照上傳圖片

1、呼叫本地相機拍照並上傳圖片 Intent cameraIntent = new Intent( "android.media.action.IMAGE_CAPTURE"); startActivityForResult(cameraIntent, m

Android demo--呼叫系統相機拍照顯示圖片為黑白

1.環境搭建 作業系統是Mac OS,一年多以前寫Android的時候用的還是Eclipse,作業系統是Windows,記得環境很難搭建,總是會有錯誤,所以面試的時候要求完成這個Demo還是有一點點虛。不過用Baidu和Google查了怎麼搭建環境之後,發現有了官方的IDE

Android WebView 選擇圖片上傳(呼叫相機拍照/相簿/選擇檔案)

前言: 這個功能其實我才剛接觸。不熟...在這個給大家提供的都是經過本人驗證之後的一些案例。可以在專案中跑的。 最近專案嵌入混合開發,都是使用WebView來跳轉,頁面展示。有用到這個圖片上傳的功能。 原本是一臉懵的,查了很多資料看一些大神都寫的很多程式碼很複雜(原諒我

Android呼叫相機實現拍照裁剪圖片,呼叫手機中的相簿圖片裁剪圖片

在 Android應用中,很多時候我們需要實現上傳圖片,或者直接呼叫手機上的拍照功能拍照處理然後直接顯示並上傳功能,下面將講述呼叫相機拍照處理圖片然後顯示和呼叫手機相簿中的圖片處理然後顯示的功能,要想實現上傳功能,一般都是上傳到資料庫中,將imageView中的圖片取出來然

Android呼叫系統相機拍照儲存到SD卡的兩種實現方式

1.呼叫照相機時通過putExtra的方式直接指定儲存路徑 String FilePath = "/sdcard/pic/"; File file = new File(FilePath); file.mkdirs();// 建立資料夾 Intent intent

Android瀏覽器中通過WebView呼叫相機拍照/選擇檔案 上傳到伺服器

                最近做的一個專案中,有這樣一個要求,在瀏覽器中呼叫系統的拍照功能或者選擇檔案,然後將檔案上傳到伺服器,類似修改頭像。        簡單而言,就是在一個html頁面中有這樣一段程式碼 <input class="filePrew" type="file" capture=

Android開發之呼叫相機拍照與本地相簿選擇圖片

使用者看到的介面如下,也是我的xml介面: demo中有一個按鈕,點選彈出對話方塊提示使用者選擇:拍照、相簿。如下圖: 點選拍照,會開啟手機相機開始拍照,拍攝完後可以裁剪,如下圖: 這時候你可以直接點選確定,也可

Android 7.0呼叫相機拍照 報錯 FileUriExposedException

摘自郭霖 《第一行程式碼》 從Android 7.0系統開始,直接使用本地真實路徑的Uri,被認為是不安全的,會丟擲一個FileUriExposedException異常,,而FileProvider則是一種特殊的內容提供器,它使用了和內容提供器類似的機制來對

android呼叫相機後自動拍照

  b 合理使用requestAnimationFrame動畫代替setTimeout 微服務 基於Mongodb分散式集做資料分析時,當前命令 IBM DB2 .比如路由器即不能有這種情況出現,贏得了各大平臺的青睞ANSWER是資源呈現出來的形式.   如果1眼看到全

android呼叫照相機拍照獲取照片

1.呼叫系統的照相機程式 Intent intent = newIntent(MediaStore.ACTION_IMAGE_CAPTURE);  startActivityForResult(intent,TAKE_PHOTO_WITH_DATA); 2.在onActivityResult中處理返回