Android常用例項——擷取APP當前介面(可帶圖片、文字水印)
我們經常會看到很多APP會有一個截圖的功能,雖然現在很多手機本身已經有截圖的功能了,但是截圖後不能加水印,這也是不小的麻煩,今天我們聊聊怎麼截圖APP當前介面自己想要的內容,並且加上水印後儲存。
先看看我們的總體介面的效果。
這個就是在我點選儲存按鈕後儲存圖片的效果截圖。下方是加了一個文字水印,當然我們還可以實現圖片水印。
這一次我們就採用循序漸進的方式來一步步探索這一個功能。先看怎麼截圖和儲存。
我先談談思路,既然是一張截圖,那首先就是一張圖,任何一張圖都有寬和高,這是基本屬性,那麼這個寬跟高是什麼呢?就是我們所要的內容的高和寬,我們直接計算整個父級容器的高寬即可,然後這裡要介紹一個系統的方法:
Bitmap.createBitmap(width, height, config)
看名字就很清楚了,原來這個是建立一個Bitmap,那就好辦了,這裡需要傳入高、寬、還有一個bitmap的配置,這裡我們直接傳入Bitmap.Config.ARGB_8888。
然後呼叫父級容器的draw方法繪製就行了。完整的程式碼是:
/**
* 把佈局轉換成bitmap
* @param scrollView
* @return bitmap
*/
public Bitmap getBitmapByView(ScrollView scrollView) {
Bitmap bitmap = null ;
// 建立對應大小的bitmap
bitmap = Bitmap.createBitmap(scrollView.getWidth(), scrollView.getHeight(),
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
scrollView.draw(canvas);
// 測試輸出
FileOutputStream out = null;
try {
out = new FileOutputStream("/sdcard/screen_test.png");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
if (null != out) {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
我們這裡的父級容器是ScrollView ,所以使用的是ScrollView 的寬、高。另外我這裡模擬了一下儲存,看不懂也沒關係,實際並沒用上。
下面看看我們是怎麼執行儲存操作的。
/**
* 儲存圖片至/sdcard/myFolder資料夾下
* @param bmp
*/
public void saveCroppedImage(Bitmap bmp) {
File file = new File("/sdcard/myFolder");
if (!file.exists())
file.mkdir();
long time= System.currentTimeMillis();
file = new File("/sdcard/"+time+".jpg".trim());
String fileName = file.getName();
String mName = fileName.substring(0, fileName.lastIndexOf("."));
String sName = fileName.substring(fileName.lastIndexOf("."));
newFilePath = "/sdcard/myFolder" + "/" + mName + sName;
file = new File(newFilePath);
try {
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
bmp.compress(CompressFormat.JPEG, 50, fos);
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
這裡是一個儲存的操作了,把剛剛生成的bitmap傳入這個方法就可以了,不是很難,我們學檔案操作的時候都接觸過,我這裡是把檔案儲存在了/sdcard/myFolder目錄下,這個目錄可以自定義,另外我這裡的檔名是採用當前的系統時間加上字尾名。
檔案截圖跟檔案儲存都有了,下面可以看看怎麼生成水印了,先看看生成生成照片的水印。看看程式碼:
/**
* 有水印的儲存
* @param photo 當前截圖的bitmap
* @param mark 水印的圖片
* @param image 控制元件
* @return
*/
public Bitmap addTagUtil(Bitmap photo){
Bitmap photoMark = Bitmap.createBitmap(photo.getWidth(), photo.getHeight(), Config.ARGB_8888);
Bitmap mark= BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher);
Canvas canvas = new Canvas(photoMark);
canvas.drawBitmap(photo, 0, 0, null);
Bitmap bitmapMark =mark.copy(Bitmap.Config.ARGB_8888, true);
canvas.drawBitmap(bitmapMark, photo.getWidth() - bitmapMark.getWidth()-10, photo.getHeight() - bitmapMark.getHeight(), null);
canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.restore();
return photoMark;
}
這裡實際上也是在建立一個bitmap,只是我們用canvas去繪製的時候有重疊的部分。一般的,水印都是在圖片的右下角,所以我們也是按照套路來。這個方法返回的bitmap就是一個已經有圖片水印的bitmap了,同樣可以呼叫saveCroppedImage(bitmap)來儲存。
接下來再看看繪製有文字水印的截圖:
/**
* 新增文字到圖片,類似水印文字。
* @param gContext
* @param gResId
* @param gText
* @return
*/
public Bitmap drawTextToBitmap(Bitmap bitmap, String gText) {
Resources resources = mContext.getResources();
float scale = resources.getDisplayMetrics().density;
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
if (bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.parseColor("#458EFD"));
paint.setTextSize((int) (3 * scale*5));
paint.setShadowLayer(1f, 0f, 1f, Color.WHITE);
Rect bounds = new Rect();
paint.getTextBounds(gText, 0, gText.length(), bounds);
int x = (bitmap.getWidth() - bounds.width())/10*9 ;
int y = (bitmap.getHeight() + bounds.height())/10*9;
canvas.drawText(gText, x , y, paint);
return bitmap;
}
可以看到這實際上也是一個套路,我們把文字傳進去,在前面我們講自定義View的時候接觸過很多繪製文字的操作,所以我覺得也不需要再去解釋怎麼繪製文字了。同樣的這裡返回一個bitmap,那麼這個bitmap就是帶有文字的水印了。
當然,為了讓截圖效果更逼真,我們還可以加上一個屬性動畫:
/**
* 定義動畫
* @param mBg 父級容器
* @param mImage 將要顯示的圖片控制元件
* @param bitmap 圖片
*/
public void startAnim(final LinearLayout mBg,final ImageView mImage,final Bitmap bitmap){
//設定為半透明
mBg.setBackgroundColor(Color.parseColor("#e0000000"));
PropertyValuesHolder values1=PropertyValuesHolder.ofFloat("alpha", 0f,1f,0.5f);
PropertyValuesHolder values2=PropertyValuesHolder.ofFloat("scaleX", 1,3/4f);
PropertyValuesHolder values3=PropertyValuesHolder.ofFloat("scaleY", 1,3/4f);
ObjectAnimator animator=ObjectAnimator.ofPropertyValuesHolder(mImage, values1,values2,values3);
animator.setDuration(1000);
animator.setInterpolator(new DecelerateInterpolator());
animator.start();
animator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator arg0) {
}
@Override
public void onAnimationRepeat(Animator arg0) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animator arg0) {
mImage.setVisibility(View.INVISIBLE);
//重置背景
mBg.setBackgroundColor(Color.parseColor("#00000000"));
}
@Override
public void onAnimationCancel(Animator arg0) {
// TODO Auto-generated method stub
}
});
}
這樣就可以實現截圖的功能了。下面我貼上全部程式碼:
佈局檔案:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.ssssss.MainActivity" >
<TextView
android:id="@+id/save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#458EFD"
android:gravity="center"
android:padding="10dip"
android:text="儲存"
android:textColor="#ffffff"
android:textSize="20sp" />
<ScrollView
android:id="@+id/mScrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#eeeeee" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dip"
android:text="花淡淡,水悠悠,葉田田。那又是誰斑駁了這絕世的花顏,攜一簾幽夢,
盈一袖輕雲,掬一滴清泉,攬一縷清風,嫻靜優雅,柔情繾綣,尾隨而來?蓮花初開,微風徐來,沁心的荷香,
詩意了歲月,徜徉了人間,嗅著這若有若無的淡淡荷花香,一股通靈的感覺如遊絲般慢慢的浸潤了心扉,
躁動的心兒也彷彿跟著悄然安靜了下來。
“陌上欣欣青綠邈,歲歲年年,輪轉無終了。”那又是誰蹙一彎峨眉,流轉於瀲灩碧波;嬉一襲水袖,驚鴻在風塵闕歌?逸動的漣漪,惑起的水滴,風動荷香,花開蓮池。那又是誰羞答答的捧起那一宛粉,一宛白,以珠水空靈的韻味勾勒出那娓娓垂涎流香?風過,心頭一抹芬芳,指尖一縷餘香。是蓮花開了嗎?是蓮花她處處開了嗎? 那我為何只見花開,不見你來?
少時,小雨隨風而至。傾時,雲水之巔,清風細雨,煙霧迷彌,翠玉輕翻,紅粉留痕,氤氳的蓮池瞬時漾起一行行醉人的音符。“蕊中千滴淚,心裡萬條絲。”滴滴細雨,悠悠情思,那淡淡風痕處,款款崖暖時,你可曉蓮花她已處處開?氤氳煙雨中,毓秀淡雅刻,那你又可懂迷離了誰的清波凝眸?馨香的花瓣,百年的凝望,千年的等待,“荷花頻頻,楊柳依依”,
“三生兩會,兩世相逢”。既然,山和水可以彼此相忘,星和月可以流光相皎。風華只是一指流沙,蒼老只是一段年華。那麼,心兒在您的世界,您的天空可真的有我?" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dip"
android:text="“陌上欣欣青綠邈,歲歲年年,輪轉無終了。" />
</LinearLayout>
<LinearLayout
android:id="@+id/image_bg"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@+id/jietu_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
</LinearLayout>
</RelativeLayout>
</ScrollView>
</LinearLayout>
主介面:
public class MainActivity extends Activity {
private TextView button;
private ScrollView mScrollView;
private ImageView mImage;
private LinearLayout mBg;
private Utils utils;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
utils=new Utils(MainActivity.this);
initView();
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Bitmap bitmap = utils.getBitmapByView(mScrollView);//這裡返回的是沒有水印的圖片
mImage.setVisibility(View.VISIBLE);
utils.startAnim(mBg,mImage,bitmap);
// bitmap= utils.addTagUtil(bitmap);//返回有圖片水印
bitmap=utils.drawTextToBitmap(bitmap, "http://blog.csdn.net/qq_25193681");//返回有文字的水印
utils.saveCroppedImage(bitmap);
mImage.setImageBitmap(bitmap);
mImage.setVisibility(View.VISIBLE);
}
});
}
private void initView() {
mScrollView=(ScrollView) findViewById(R.id.mScrollview);
button=(TextView) findViewById(R.id.save);
mBg=(LinearLayout) findViewById(R.id.image_bg);
mImage=(ImageView) findViewById(R.id.jietu_image);
}
}
工具類;
public class Utils {
private Context mContext;
public Utils(Context mContext) {
this.mContext = mContext;
}
private static String newFilePath = "";
/**
* 定義動畫
* @param mBg 父級容器
* @param mImage 將要顯示的圖片控制元件
* @param bitmap 圖片
*/
public void startAnim(final LinearLayout mBg,final ImageView mImage,final Bitmap bitmap){
//設定為半透明
mBg.setBackgroundColor(Color.parseColor("#e0000000"));
PropertyValuesHolder values1=PropertyValuesHolder.ofFloat("alpha", 0f,1f,0.5f);
PropertyValuesHolder values2=PropertyValuesHolder.ofFloat("scaleX", 1,3/4f);
PropertyValuesHolder values3=PropertyValuesHolder.ofFloat("scaleY", 1,3/4f);
ObjectAnimator animator=ObjectAnimator.ofPropertyValuesHolder(mImage, values1,values2,values3);
animator.setDuration(1000);
animator.setInterpolator(new DecelerateInterpolator());
animator.start();
animator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator arg0) {
}
@Override
public void onAnimationRepeat(Animator arg0) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animator arg0) {
mImage.setVisibility(View.INVISIBLE);
//重置背景
mBg.setBackgroundColor(Color.parseColor("#00000000"));
}
@Override
public void onAnimationCancel(Animator arg0) {
// TODO Auto-generated method stub
}
});
}
/**
* 把佈局轉換成bitmap
* @param scrollView
* @return bitmap
*/
public Bitmap getBitmapByView(ScrollView scrollView) {
Bitmap bitmap = null;
// 建立對應大小的bitmap
bitmap = Bitmap.createBitmap(scrollView.getWidth(), scrollView.getHeight(),
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
scrollView.draw(canvas);
// 測試輸出
FileOutputStream out = null;
try {
out = new FileOutputStream("/sdcard/screen_test.png");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
if (null != out) {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
/**
* 儲存圖片至/sdcard/myFolder資料夾下
* @param bmp
*/
public void saveCroppedImage(Bitmap bmp) {
File file = new File("/sdcard/myFolder");
if (!file.exists())
file.mkdir();
long time= System.currentTimeMillis();
file = new File("/sdcard/"+time+".jpg".trim());
String fileName = file.getName();
String mName = fileName.substring(0, fileName.lastIndexOf("."));
String sName = fileName.substring(fileName.lastIndexOf("."));
newFilePath = "/sdcard/myFolder" + "/" + mName + sName;
file = new File(newFilePath);
try {
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
bmp.compress(CompressFormat.JPEG, 50, fos);
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 有水印的儲存
* @param photo 當前截圖的bitmap
* @param mark 水印的圖片
* @param image 控制元件
* @return
*/
public Bitmap addTagUtil(Bitmap photo){
Bitmap photoMark = Bitmap.createBitmap(photo.getWidth(), photo.getHeight(), Config.ARGB_8888);
Bitmap mark= BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher);
Canvas canvas = new Canvas(photoMark);
canvas.drawBitmap(photo, 0, 0, null);
Bitmap bitmapMark =mark.copy(Bitmap.Config.ARGB_8888, true);
canvas.drawBitmap(bitmapMark, photo.getWidth() - bitmapMark.getWidth()-10, photo.getHeight() - bitmapMark.getHeight(), null);
canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.restore();
return photoMark;
}
/**
* 新增文字到圖片,類似水印文字。
* @param gContext
* @param gResId
* @param gText
* @return
*/
public Bitmap drawTextToBitmap(Bitmap bitmap, String gText) {
Resources resources = mContext.getResources();
float scale = resources.getDisplayMetrics().density;
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
if (bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.parseColor("#458EFD"));
paint.setTextSize((int) (3 * scale*5));
paint.setShadowLayer(1f, 0f, 1f, Color.WHITE);
Rect bounds = new Rect();
paint.getTextBounds(gText, 0, gText.length(), bounds);
int x = (bitmap.getWidth() - bounds.width())/10*9 ;
int y = (bitmap.getHeight() + bounds.height())/10*9;
canvas.drawText(gText, x , y, paint);
return bitmap;
}
}
當然,我覺得小夥伴光貼上複製,那肯定不過癮,下面我再把原始碼貼上:原始碼
相關推薦
Android常用例項——擷取APP當前介面(可帶圖片、文字水印)
我們經常會看到很多APP會有一個截圖的功能,雖然現在很多手機本身已經有截圖的功能了,但是截圖後不能加水印,這也是不小的麻煩,今天我們聊聊怎麼截圖APP當前介面自己想要的內容,並且加上水印後儲存。 先看看我們的總體介面的效果。 這個就是在我點選儲存按鈕後儲
推薦幾個天氣Api介面 (可在小程式中使用)
國內的地圖軟體基本是支援查詢天氣的唯獨騰訊地圖不支援天氣查詢,當然其他天氣平臺提供的介面也是夠個人使用的 1、高德地圖(高德地圖開發者平臺) 支援獲取實時天氣資料 2、百度地圖(百度地圖開放平臺)
Android開發基礎 呼叫相機 系統相簿(並對圖片進行壓縮處理)
前言:做了好久的安卓開發了,一直想寫點東西分享下。但是又總覺得自己學的還不夠好,說出來有可能會誤導人,所以一直都沒有發. 最近在專案中遇到了最多的問題就是關於圖片的問題,應該算是比較簡單的了,拿出來跟大家分享下。(第一次寫部落格,希望給位大神能夠多提意見^_^)
Unity呼叫百度地圖(可實現模型、UI覆蓋)
相信如果在看我這篇部落格的朋友應該都已經將度娘上【Unity】+【百度地圖】下的搜尋結果的都看過一遍了,大概列舉一下: 1、使用百度地圖的Android SDK,將自己寫好的建立地圖View的方法打成jar包,並在Unity中呼叫。 2、使用百度地圖的靜
輕量級web富文字編輯器(可帶圖片上傳)
業務需求: 通過後臺編輯文章和圖片,上傳到前端介面,展示新聞訊息模組。這個時候,需要一款簡潔的編輯器,百度編輯器是最常用的一種,但是功能太過於複雜,而wangEditor - 輕量級web富文字編輯器,配置方便,使用簡單。支援 IE10+ 瀏覽器,值得擁有。 wa
Android APP啟動介面(動畫)
首先定義基類BaseActivity public abstract class BaseActivity extends AppCompatActivity { 實現類SplashActivity public class SplashActiv
Android 端天氣預報APP的實現(一)天氣顯示介面之上下滑動
最近參加了一個小比賽,選了天氣預報APP這個題目,最初選擇它,是想練練網路資料獲取和資料解析方面的知識,後來發現倒是學到了不少介面的東西。下面我來一步步講解一下我是怎麼完成的吧~ 首先,天氣預報最直觀的部分應該就是天氣顯示介面了,這裡,我想做成可以有上下滑動的
Android Studio 一個完整的APP實例(附源碼和數據庫)
first auto engine cos 快速 實例 art qlite 功能介紹 前言: 這是我獨立做的第一個APP,是一個記賬本APP。 This is the first APP, I‘ve ever done on my own. It‘s a accountbo
win10建立Ubuntu18.04子系統,安裝常用軟體以及圖形介面(包括win10遠端桌面連線Ubuntu)
https://blog.csdn.net/li528405176/article/details/82263534 一、開啟win10子系統 [ Windows Subsystem for Linux(WSL)] 準備工作 設定——>更新和安全——>針對開發人員——>
Android安全/開發基礎--10--圖形介面(UI)和碎片(Fragment)(上)
10-1、View理論 View的事件體系 View是Android中所有控制元件的基類。ViewGroup是控制元件組,內部包含了許多控制元件。 view的四個位置屬性:top、left、right、bottom MotionEvent:手指接觸屏幕後產生的
Android安全/開發基礎--11--圖形介面(UI)和碎片(Fragment)(下)
10-9、UI fragment與fragment管理器 採用fragment而不是activity來管理應用UI,可繞開Android系統activity使用規則的限制。fragment是一種控制器物件,activity可委派它執行任務。這些任務通常就是管理使
Android 常用的地球經緯度轉換公里(km)計算工具類
地球赤道上環繞地球一週走一圈共40075.04公里,而@一圈分成360°,而每1°(度)有60,每一度一秒在赤道上的長度計算如下: 40075.04km/360°=111.31955km 111.31955km/60=1.8553258km=1855.3m 任意兩點距離計算公式見程
Android總結Handler非同步更新UI介面(轉載)
轉載地址:https://blog.csdn.net/qq_21004057/article/details/51582412 本篇文章通過三種方式來實現UI控制元件的更新,Handler非同步更新UI在安卓開發中最常用也非常實在。這篇文章注重實現思路,所以我就不在介面方面進行美化了,都是最原始
win10建立Ubuntu16.04子系統,安裝常用軟體以及圖形介面(包括win10遠端桌面連線Ubuntu)
目錄 一、開啟win10子系統 [ Windows Subsystem for Linux(WSL)] 準備工作 設定——>更新和安全——>針對開發人員——>開發人員模式 控制面板——>程式——>程式和
Android 常用開發工具類之 SPUtils (SharedPreference 工具類)
開發過程中難免會遇到如持久儲存使用者資訊等需求,而由於資料量很少感覺使用 Sql 有些殺雞用牛刀的感覺也有些累贅。所以善於使用 SharedPreference 可以幫助我們在本地儲存一些資料量少,又使用很頻繁的東西。 SPUtils 一個可以幫助我們很簡潔的使用 Sha
Android開發者編寫自己的API介面(上)
序 作為一名Android開發人員,想要實現對一些資料的操作和展示,可以通過一些提供Api介面的網站去獲取,雖然Api市場上種類繁多,不過別人提供的介面未必就是自己想要的,到最後,還是得自己去實現Api介面。 畢竟,最瞭解自己需求的人,還得是自己。 準
Android如何程式設計設定APP安裝位置(外部儲存或內部儲存)?
Beginning with API Level 8, you can allow your application to be installed on the external storage (for example, the device's SD card).
Android開發者編寫自己的API介面(下)
前言 在上一篇Android開發者編寫自己的API介面(上)中,已經介紹瞭如何搭建一個基本的開發環境,以及介面的編寫,最後是能夠成功執行的。 這一篇將更進一步,主要解決下面兩個問題: ①:如何讓後臺專案執行在TomCat上 ②:如何在雲伺服器上部署自己的
Android Studio 開發時 App機器人位置(select run/debug Configuration)位置出現紅叉導致程式不能執行的解決方法
相信很多Android開發者剛從Eclipse更換為Andriod Studio時,都會被Studio各種無腦問題虐過。 我也是如此。 eg:1、建立專案 Gradle 總是報錯,專案不能執行 2、就是標題說的,App小機器人的位置出現紅叉,但是程式碼沒有任何錯,就是不
【Android】關於不刪除當前介面資料重新整理列表問題
前言: App中有一模組為應用中心,改版之後低仿了ZFB中應用管理的模式,但是就按照我們之前的開發方式,首頁重新整理時會將資料清空一下然後重新賦值給list填充到介面上,甲方領導看了之後感覺體驗不是很好,因為IOS端是不清空資料重新整理的,所以要求Android