Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 屏幕截屏方法匯總

Android 屏幕截屏方法匯總

編輯:關於Android編程

1、直接使用getWindow().getDecorView().getRootView()

直接使用getWindow().getDecorView().getRootView()是獲取當前屏幕的activity。然而對於系統狀態欄的信息是截不了,出現一條空白的。如下圖:

 

主要到沒,有一條白色邊就是系統狀態欄。看一下代碼,很簡單都加了注釋了。

//這種方法狀態欄是空白,顯示不了狀態欄的信息 
private void saveCurrentImage() 
{ 
//獲取當前屏幕的大小 
int width = getWindow().getDecorView().getRootView().getWidth(); 
int height = getWindow().getDecorView().getRootView().getHeight(); 
//生成相同大小的圖片 
Bitmap temBitmap = Bitmap.createBitmap( width, height, Config.ARGB_8888 ); 
//找到當前頁面的跟布局 
View view = getWindow().getDecorView().getRootView(); 
//設置緩存 
view.setDrawingCacheEnabled(true); 
view.buildDrawingCache(); 
//從緩存中獲取當前屏幕的圖片 
temBitmap = view.getDrawingCache(); 
//輸出到sd卡 
if (FileIOUtil.getExistStorage()) { 
FileIOUtil.GetInstance().onFolderAnalysis(FileIOUtil.GetInstance().getFilePathAndName()); 
File file = new File(FileIOUtil.GetInstance().getFilePathAndName()); 
try { 
if (!file.exists()) { 
file.createNewFile(); 
} 
FileOutputStream foStream = new FileOutputStream(file); 
temBitmap.compress(Bitmap.CompressFormat.PNG, 100, foStream); 
foStream.flush(); 
foStream.close(); 
} catch (Exception e) { 
Log.i("Show", e.toString()); 
} 
} 
}

2、自定義view控件的截圖

自定義view控件都是繼承view的嗎,那麼就有可以獲取寬度,高度。生成圖片,把它繪制出來的。我拿了直接寫的自定義隨機驗證碼的例子來截圖。

//保存自定義view的截圖 
private void saveCustomViewBitmap() { 
//獲取自定義view圖片的大小 
Bitmap temBitmap = Bitmap.createBitmap(mCodeView.getWidth(), mCodeView.getHeight(), Config.ARGB_8888); 
//使用Canvas,調用自定義view控件的onDraw方法,繪制圖片 
Canvas canvas = new Canvas(temBitmap); 
mCodeView.onDraw(canvas); 
//輸出到sd卡 
if (FileIOUtil.getExistStorage()) { 
FileIOUtil.GetInstance().onFolderAnalysis(FileIOUtil.GetInstance().getFilePathAndName()); 
File file = new File(FileIOUtil.GetInstance().getFilePathAndName()); 
try { 
if (!file.exists()) { 
file.createNewFile(); 
} 
FileOutputStream foStream = new FileOutputStream(file); 
temBitmap.compress(Bitmap.CompressFormat.PNG, 100, foStream); 
foStream.flush(); 
foStream.close(); 
Toast.makeText(MainActivity.this, "截屏文件已保存至" + FileIOUtil.GetInstance().getFilePathAndName(), Toast.LENGTH_LONG).show(); 
} catch (Exception e) { 
Log.i("Show", e.toString()); 
} 
} 
}

有人根據這種思路,自定義整個布局的view,然後來截圖。也是夠拼的,我有點不贊成這樣使用。

3、基於android ddmlib截屏

這個是java寫的一個類,入口是mian函數。那麼這種實現方式是要android連接著電腦,還需要android設備調試。這種不多說,搞android開發都懂。太麻煩了,我們也不使用。

package com.example.screenshot; 
import java.io.IOException; 
import java.text.SimpleDateFormat; 
import java.util.Date; 
import com.android.ddmlib.AdbCommandRejectedException; 
import com.android.ddmlib.AndroidDebugBridge; 
import com.android.ddmlib.IDevice; 
import com.android.ddmlib.RawImage; 
import com.android.ddmlib.TimeoutException; 
public class ScreenShoddmlib { 
private BufferedImage image = null; 
/** 
* @param args 
*/ 
public static void main(String[] args) { 
// TODO Auto-generated method stub 
AndroidDebugBridge.init(false); // 
ScreenShoddmlib screenshot = new ScreenShoddmlib(); 
IDevice device = screenshot.getDevice(); 
for (int i = 0; i < 10; i++) { 
Date date=new Date(); 
SimpleDateFormat df=new SimpleDateFormat("MM-dd-HH-mm-ss"); 
String nowTime = df.format(date); 
screenshot.getScreenShot(device, "Robotium" + nowTime); 
try { 
Thread.sleep(1000); 
} catch (InterruptedException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
} 
} 
} 
public void getScreenShot(IDevice device,String filename) { 
RawImage rawScreen = null; 
try { 
rawScreen = device.getScreenshot(); 
} catch (TimeoutException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
} catch (AdbCommandRejectedException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
} catch (IOException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
} 
if (rawScreen != null) { 
Boolean landscape = false; 
int width2 = landscape ? rawScreen.height : rawScreen.width; 
int height2 = landscape ? rawScreen.width : rawScreen.height; 
if (image == null) { 
image = new BufferedImage(width2, height2, 
BufferedImage.TYPE_INT_RGB); 
} else { 
if (image.getHeight() != height2 || image.getWidth() != width2) { 
image = new BufferedImage(width2, height2, 
BufferedImage.TYPE_INT_RGB); 
} 
} 
int index = 0; 
int indexInc = rawScreen.bpp >> 3; 
for (int y = 0; y < rawScreen.height; y++) { 
for (int x = 0; x < rawScreen.width; x++, index += indexInc) { 
int value = rawScreen.getARGB(index); 
if (landscape) 
image.setRGB(y, rawScreen.width - x - 1, value); 
else 
image.setRGB(x, y, value); 
} 
} 
try { 
ImageIO.write((RenderedImage) image, "PNG", new File("D:/" 
+ filename + ".jpg")); 
} catch (IOException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
} 
} 
} 
/** 
* 獲取得到device對象 
* @return 
*/ 
private IDevice getDevice(){ 
IDevice device; 
AndroidDebugBridge bridge = AndroidDebugBridge 
.createBridge("adb", true);//如果代碼有問題請查看API,修改此處的參數值試一下 
waitDevicesList(bridge); 
IDevice devices[] = bridge.getDevices(); 
device = devices[0]; 
return device; 
} 
/** 
* 等待查找device 
* @param bridge 
*/ 
private void waitDevicesList(AndroidDebugBridge bridge) { 
int count = 0; 
while (bridge.hasInitialDeviceList() == false) { 
try { 
Thread.sleep(500); 
count++; 
} catch (InterruptedException e) { 
} 
if (count > 240) { 
System.err.print("等待獲取設備超時"); 
break; 
} 
} 
}

4、使用adb命令

需要系統權限,在APK中調用“adb shell screencap -pfilepath” 命令

需要獲得系統權限:

1、 在AndroidManifest.xml文件中添加 <uses-permissionandroid:name="android.permission.READ_FRAME_BUFFER"/>

2、修改APK為系統權限,將APK放到源碼中編譯, 修改Android.mk LOCAL_CERTIFICATE := platform
在這裡我要說一下,搞過jni調用就知道Android.mk的作用。此舉也是麻煩,效果也不是很好。

public void takeScreenShot(){ 
String mSavedPath = Environment.getExternalStorageDirectory()+File. separator + "screenshot.png" ; 
try { 
Runtime. getRuntime().exec("screencap -p " + mSavedPath); 
} catch (Exception e) { 
e.printStackTrace(); 
}

5、看一下系統截屏是怎樣的

相信大家都知道,三星的機子是同時按下 home鍵 + 電源鍵 3秒截圖。
如果沒有home鍵的機子是按下 音量鍵向下那個 + 電源鍵 3秒截圖的。
獲取物理鍵盤按下的源碼:PhoneWindowManager.java

// Handle special keys. 
switch (keyCode) { 
case KeyEvent.KEYCODE_VOLUME_DOWN: 
case KeyEvent.KEYCODE_VOLUME_UP: 
case KeyEvent.KEYCODE_VOLUME_MUTE: { 
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { 
if (down) { 
if (isScreenOn && !mVolumeDownKeyTriggered 
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { 
mVolumeDownKeyTriggered = true; 
mVolumeDownKeyTime = event.getDownTime(); 
mVolumeDownKeyConsumedByScreenshotChord = false; 
cancelPendingPowerKeyAction(); 
interceptScreenshotChord(); 
} 
} else { 
mVolumeDownKeyTriggered = false; 
cancelPendingScreenshotChordAction(); 
} 
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { 
if (down) { 
if (isScreenOn && !mVolumeUpKeyTriggered 
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { 
mVolumeUpKeyTriggered = true; 
cancelPendingPowerKeyAction(); 
cancelPendingScreenshotChordAction(); 
} 
} else { 
mVolumeUpKeyTriggered = false; 
cancelPendingScreenshotChordAction(); 
} 
} 
if (down) { 
ITelephony telephonyService = getTelephonyService(); 
if (telephonyService != null) { 
try { 
if (telephonyService.isRinging()) { 
// If an incoming call is ringing, either VOLUME key means 
// "silence ringer". We handle these keys here, rather than 
// in the InCallScreen, to make sure we'll respond to them 
// even if the InCallScreen hasn't come to the foreground yet. 
// Look for the DOWN event here, to agree with the "fallback" 
// behavior in the InCallScreen. 
Log.i(TAG, "interceptKeyBeforeQueueing:" 
+ " VOLUME key-down while ringing: Silence ringer!"); 
// Silence the ringer. (It's safe to call this 
// even if the ringer has already been silenced.) 
telephonyService.silenceRinger(); 
// And *don't* pass this key thru to the current activity 
// (which is probably the InCallScreen.) 
result &= ~ACTION_PASS_TO_USER; 
break; 
} 
if (telephonyService.isOffhook() 
&& (result & ACTION_PASS_TO_USER) == 0) { 
// If we are in call but we decided not to pass the key to 
// the application, handle the volume change here. 
handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); 
break; 
} 
} catch (RemoteException ex) { 
Log.w(TAG, "ITelephony threw RemoteException", ex); 
} 
} 
if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) { 
// If music is playing but we decided not to pass the key to the 
// application, handle the volume change here. 
handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode); 
break; 
} 
} 
break; 
} 
case KeyEvent.KEYCODE_ENDCALL: { 
result &= ~ACTION_PASS_TO_USER; 
if (down) { 
ITelephony telephonyService = getTelephonyService(); 
boolean hungUp = false; 
if (telephonyService != null) { 
try { 
hungUp = telephonyService.endCall(); 
} catch (RemoteException ex) { 
Log.w(TAG, "ITelephony threw RemoteException", ex); 
} 
} 
interceptPowerKeyDown(!isScreenOn || hungUp); 
} else { 
if (interceptPowerKeyUp(canceled)) { 
if ((mEndcallBehavior 
& Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) { 
if (goHome()) { 
break; 
} 
} 
if ((mEndcallBehavior 
& Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) { 
result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP; 
} 
} 
} 
break; 
} 
case KeyEvent.KEYCODE_POWER: { 
result &= ~ACTION_PASS_TO_USER; 
if (down) { 
if (isScreenOn && !mPowerKeyTriggered 
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { 
mPowerKeyTriggered = true; 
mPowerKeyTime = event.getDownTime(); 
interceptScreenshotChord(); 
} 
ITelephony telephonyService = getTelephonyService(); 
boolean hungUp = false; 
if (telephonyService != null) { 
try { 
if (telephonyService.isRinging()) { 
// Pressing Power while there's a ringing incoming 
// call should silence the ringer. 
telephonyService.silenceRinger(); 
} else if ((mIncallPowerBehavior 
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 
&& telephonyService.isOffhook()) { 
// Otherwise, if "Power button ends call" is enabled, 
// the Power button will hang up any current active call. 
hungUp = telephonyService.endCall(); 
} 
} catch (RemoteException ex) { 
Log.w(TAG, "ITelephony threw RemoteException", ex); 
} 
} 
interceptPowerKeyDown(!isScreenOn || hungUp 
|| mVolumeDownKeyTriggered || mVolumeUpKeyTriggered); 
} else { 
mPowerKeyTriggered = false; 
cancelPendingScreenshotChordAction(); 
if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) { 
result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP; 
} 
mPendingPowerKeyUpCanceled = false; 
} 
break; 
}

響應截屏的方法:

private void interceptScreenshotChord() { 
if (mScreenshotChordEnabled 
&& mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) { 
final long now = SystemClock.uptimeMillis(); 
if (now <= mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS 
&& now <= mPowerKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) { 
mVolumeDownKeyConsumedByScreenshotChord = true; 
cancelPendingPowerKeyAction(); 
mHandler.postDelayed(mScreenshotChordLongPress, getScreenshotChordLongPressDelay()); 
} 
} 
} 
private long getScreenshotChordLongPressDelay() { 
if (mKeyguardMediator.isShowing()) { 
// Double the time it takes to take a screenshot from the keyguard 
return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * 
ViewConfiguration.getGlobalActionKeyTimeout()); 
} else { 
return ViewConfiguration.getGlobalActionKeyTimeout(); 
} 
}

接受響應的服務

private final Runnable mScreenshotChordLongPress = new Runnable() { 
public void run() { 
takeScreenshot(); 
} 
}; 
private void takeScreenshot() { 
synchronized (mScreenshotLock) { 
if (mScreenshotConnection != null) { 
return; 
} 
ComponentName cn = new ComponentName("com.android.systemui", 
"com.android.systemui.screenshot.TakeScreenshotService"); 
Intent intent = new Intent(); 
intent.setComponent(cn); 
ServiceConnection conn = new ServiceConnection() { 
@Override 
public void onServiceConnected(ComponentName name, IBinder service) { 
synchronized (mScreenshotLock) { 
if (mScreenshotConnection != this) { 
return; 
} 
Messenger messenger = new Messenger(service); 
Message msg = Message.obtain(null, 1); 
final ServiceConnection myConn = this; 
Handler h = new Handler(mHandler.getLooper()) { 
@Override 
public void handleMessage(Message msg) { 
synchronized (mScreenshotLock) { 
if (mScreenshotConnection == myConn) { 
mContext.unbindService(mScreenshotConnection); 
mScreenshotConnection = null; 
mHandler.removeCallbacks(mScreenshotTimeout); 
} 
} 
} 
}; 
msg.replyTo = new Messenger(h); 
msg.arg1 = msg.arg2 = 0; 
if (mStatusBar != null && mStatusBar.isVisibleLw()) 
msg.arg1 = 1; 
if (mNavigationBar != null && mNavigationBar.isVisibleLw()) 
msg.arg2 = 1; 
try { 
messenger.send(msg); 
} catch (RemoteException e) { 
} 
} 
} 
@Override 
public void onServiceDisconnected(ComponentName name) {} 
}; 
if (mContext.bindService( 
intent, conn, Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) { 
mScreenshotConnection = conn; 
mHandler.postDelayed(mScreenshotTimeout, 10000); 
} 
} 
}

啟動時這個服務 ComponentName cn = new ComponentName("com.android.systemui", "com.android.systemui.screenshot.TakeScreenshotService");

package com.android.systemui.screenshot; 
import android.app.Service; 
import android.content.Intent; 
import android.os.Handler; 
import android.os.IBinder; 
import android.os.Message; 
import android.os.Messenger; 
import android.os.RemoteException; 
public class TakeScreenshotService extends Service { 
private static final String TAG = "TakeScreenshotService"; 
private static GlobalScreenshot mScreenshot; 
private Handler mHandler = new Handler() { 
@Override 
public void handleMessage(Message msg) { 
switch (msg.what) { 
case 1: 
final Messenger callback = msg.replyTo; 
if (mScreenshot == null) { 
mScreenshot = new GlobalScreenshot(TakeScreenshotService.this); 
} 
mScreenshot.takeScreenshot(new Runnable() { 
@Override public void run() { 
Message reply = Message.obtain(null, 1); 
try { 
callback.send(reply); 
} catch (RemoteException e) { 
} 
} 
}, msg.arg1 > 0, msg.arg2 > 0); 
} 
} 
}; 
@Override 
public IBinder onBind(Intent intent) { 
return new Messenger(mHandler).getBinder(); 
}

再往下就是底層庫,需要編譯出來才能看得到了。這些就先不研究了。

6、還有部分系統源碼是截圖的

/* 
* Copyright (C) 2011 The Android Open Source Project 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/ 
package com.android.systemui.screenshot; 
import android.animation.Animator; 
import android.animation.AnimatorListenerAdapter; 
import android.animation.AnimatorSet; 
import android.animation.ValueAnimator; 
import android.animation.ValueAnimator.AnimatorUpdateListener; 
import android.app.Notification; 
import android.app.Notification.BigPictureStyle; 
import android.app.NotificationManager; 
import android.app.PendingIntent; 
import android.content.ContentResolver; 
import android.content.ContentValues; 
import android.content.Context; 
import android.content.Intent; 
import android.content.res.Resources; 
import android.graphics.Bitmap; 
import android.graphics.Canvas; 
import android.graphics.ColorMatrix; 
import android.graphics.ColorMatrixColorFilter; 
import android.graphics.Matrix; 
import android.graphics.Paint; 
import android.graphics.PixelFormat; 
import android.graphics.PointF; 
import android.media.MediaActionSound; 
import android.net.Uri; 
import android.os.AsyncTask; 
import android.os.Environment; 
import android.os.Process; 
import android.provider.MediaStore; 
import android.util.DisplayMetrics; 
import android.view.Display; 
import android.view.LayoutInflater; 
import android.view.MotionEvent; 
import android.view.Surface; 
import android.view.View; 
import android.view.ViewGroup; 
import android.view.WindowManager; 
import android.view.animation.Interpolator; 
import android.widget.ImageView; 
import com.android.systemui.R; 
import java.io.File; 
import java.io.OutputStream; 
import java.text.SimpleDateFormat; 
import java.util.Date; 
/** 
* POD used in the AsyncTask which saves an image in the background. 
*/ 
class SaveImageInBackgroundData { 
Context context; 
Bitmap image; 
Uri imageUri; 
Runnable finisher; 
int iconSize; 
int result; 
} 
/** 
* An AsyncTask that saves an image to the media store in the background. 
*/ 
class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Void, 
SaveImageInBackgroundData> { 
private static final String SCREENSHOTS_DIR_NAME = "Screenshots"; 
private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png"; 
private static final String SCREENSHOT_FILE_PATH_TEMPLATE = "%s/%s/%s"; 
private int mNotificationId; 
private NotificationManager mNotificationManager; 
private Notification.Builder mNotificationBuilder; 
private String mImageFileName; 
private String mImageFilePath; 
private long mImageTime; 
private BigPictureStyle mNotificationStyle; 
// WORKAROUND: We want the same notification across screenshots that we update so that we don't 
// spam a user's notification drawer. However, we only show the ticker for the saving state 
// and if the ticker text is the same as the previous notification, then it will not show. So 
// for now, we just add and remove a space from the ticker text to trigger the animation when 
// necessary. 
private static boolean mTickerAddSpace; 
SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data, 
NotificationManager nManager, int nId) { 
Resources r = context.getResources(); 
// Prepare all the output metadata 
mImageTime = System.currentTimeMillis(); 
String imageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(mImageTime)); 
String imageDir = Environment.getExternalStoragePublicDirectory( 
Environment.DIRECTORY_PICTURES).getAbsolutePath(); 
mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate); 
mImageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE, imageDir, 
SCREENSHOTS_DIR_NAME, mImageFileName); 
// Create the large notification icon 
int imageWidth = data.image.getWidth(); 
int imageHeight = data.image.getHeight(); 
int iconSize = data.iconSize; 
final int shortSide = imageWidth < imageHeight ? imageWidth : imageHeight; 
Bitmap preview = Bitmap.createBitmap(shortSide, shortSide, data.image.getConfig()); 
Canvas c = new Canvas(preview); 
Paint paint = new Paint(); 
ColorMatrix desat = new ColorMatrix(); 
desat.setSaturation(0.25f); 
paint.setColorFilter(new ColorMatrixColorFilter(desat)); 
Matrix matrix = new Matrix(); 
matrix.postTranslate((shortSide - imageWidth) / 2, 
(shortSide - imageHeight) / 2); 
c.drawBitmap(data.image, matrix, paint); 
c.drawColor(0x40FFFFFF); 
Bitmap croppedIcon = Bitmap.createScaledBitmap(preview, iconSize, iconSize, true); 
// Show the intermediate notification 
mTickerAddSpace = !mTickerAddSpace; 
mNotificationId = nId; 
mNotificationManager = nManager; 
mNotificationBuilder = new Notification.Builder(context) 
.setTicker(r.getString(R.string.screenshot_saving_ticker) 
+ (mTickerAddSpace ? " " : "")) 
.setContentTitle(r.getString(R.string.screenshot_saving_title)) 
.setContentText(r.getString(R.string.screenshot_saving_text)) 
.setSmallIcon(R.drawable.stat_notify_image) 
.setWhen(System.currentTimeMillis()); 
mNotificationStyle = new Notification.BigPictureStyle() 
.bigPicture(preview); 
mNotificationBuilder.setStyle(mNotificationStyle); 
Notification n = mNotificationBuilder.build(); 
n.flags |= Notification.FLAG_NO_CLEAR; 
mNotificationManager.notify(nId, n); 
// On the tablet, the large icon makes the notification appear as if it is clickable (and 
// on small devices, the large icon is not shown) so defer showing the large icon until 
// we compose the final post-save notification below. 
mNotificationBuilder.setLargeIcon(croppedIcon); 
// But we still don't set it for the expanded view, allowing the smallIcon to show here. 
mNotificationStyle.bigLargeIcon(null); 
} 
@Override 
protected SaveImageInBackgroundData doInBackground(SaveImageInBackgroundData... params) { 
if (params.length != 1) return null; 
// By default, AsyncTask sets the worker thread to have background thread priority, so bump 
// it back up so that we save a little quicker. 
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); 
Context context = params[0].context; 
Bitmap image = params[0].image; 
Resources r = context.getResources(); 
try { 
// Save the screenshot to the MediaStore 
ContentValues values = new ContentValues(); 
ContentResolver resolver = context.getContentResolver(); 
values.put(MediaStore.Images.ImageColumns.DATA, mImageFilePath); 
values.put(MediaStore.Images.ImageColumns.TITLE, mImageFileName); 
values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, mImageFileName); 
values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, mImageTime); 
values.put(MediaStore.Images.ImageColumns.DATE_ADDED, mImageTime); 
values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, mImageTime); 
values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png"); 
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); 
Intent sharingIntent = new Intent(Intent.ACTION_SEND); 
sharingIntent.setType("image/png"); 
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); 
Intent chooserIntent = Intent.createChooser(sharingIntent, null); 
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK 
| Intent.FLAG_ACTIVITY_NEW_TASK); 
mNotificationBuilder.addAction(R.drawable.ic_menu_share, 
r.getString(com.android.internal.R.string.share), 
PendingIntent.getActivity(context, 0, chooserIntent, 
PendingIntent.FLAG_CANCEL_CURRENT)); 
OutputStream out = resolver.openOutputStream(uri); 
image.compress(Bitmap.CompressFormat.PNG, 100, out); 
out.flush(); 
out.close(); 
// update file size in the database 
values.clear(); 
values.put(MediaStore.Images.ImageColumns.SIZE, new File(mImageFilePath).length()); 
resolver.update(uri, values, null, null); 
params[0].imageUri = uri; 
params[0].result = 0; 
} catch (Exception e) { 
// IOException/UnsupportedOperationException may be thrown if external storage is not 
// mounted 
params[0].result = 1; 
} 
return params[0]; 
} 
@Override 
protected void onPostExecute(SaveImageInBackgroundData params) { 
if (params.result > 0) { 
// Show a message that we've failed to save the image to disk 
GlobalScreenshot.notifyScreenshotError(params.context, mNotificationManager); 
} else { 
// Show the final notification to indicate screenshot saved 
Resources r = params.context.getResources(); 
// Create the intent to show the screenshot in gallery 
Intent launchIntent = new Intent(Intent.ACTION_VIEW); 
launchIntent.setDataAndType(params.imageUri, "image/png"); 
launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
mNotificationBuilder 
.setContentTitle(r.getString(R.string.screenshot_saved_title)) 
.setContentText(r.getString(R.string.screenshot_saved_text)) 
.setContentIntent(PendingIntent.getActivity(params.context, 0, launchIntent, 0)) 
.setWhen(System.currentTimeMillis()) 
.setAutoCancel(true); 
Notification n = mNotificationBuilder.build(); 
n.flags &= ~Notification.FLAG_NO_CLEAR; 
mNotificationManager.notify(mNotificationId, n); 
} 
params.finisher.run(); 
} 
} 
/** 
* TODO: 
* - Performance when over gl surfaces? Ie. Gallery 
* - what do we say in the Toast? Which icon do we get if the user uses another 
* type of gallery? 
*/ 
class GlobalScreenshot { 
private static final int SCREENSHOT_NOTIFICATION_ID = 789; 
private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130; 
private static final int SCREENSHOT_DROP_IN_DURATION = 430; 
private static final int SCREENSHOT_DROP_OUT_DELAY = 500; 
private static final int SCREENSHOT_DROP_OUT_DURATION = 430; 
private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370; 
private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320; 
private static final float BACKGROUND_ALPHA = 0.5f; 
private static final float SCREENSHOT_SCALE = 1f; 
private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f; 
private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f; 
private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f; 
private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f; 
private Context mContext; 
private WindowManager mWindowManager; 
private WindowManager.LayoutParams mWindowLayoutParams; 
private NotificationManager mNotificationManager; 
private Display mDisplay; 
private DisplayMetrics mDisplayMetrics; 
private Matrix mDisplayMatrix; 
private Bitmap mScreenBitmap; 
private View mScreenshotLayout; 
private ImageView mBackgroundView; 
private ImageView mScreenshotView; 
private ImageView mScreenshotFlash; 
private AnimatorSet mScreenshotAnimation; 
private int mNotificationIconSize; 
private float mBgPadding; 
private float mBgPaddingScale; 
private MediaActionSound mCameraSound; 
/** 
* @param context everything needs a context :( 
*/ 
public GlobalScreenshot(Context context) { 
Resources r = context.getResources(); 
mContext = context; 
LayoutInflater layoutInflater = (LayoutInflater) 
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
// Inflate the screenshot layout 
mDisplayMatrix = new Matrix(); 
mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null); 
mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background); 
mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot); 
mScreenshotFlash = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_flash); 
mScreenshotLayout.setFocusable(true); 
mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() { 
@Override 
public boolean onTouch(View v, MotionEvent event) { 
// Intercept and ignore all touch events 
return true; 
} 
}); 
// Setup the window that we are going to use 
mWindowLayoutParams = new WindowManager.LayoutParams( 
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0, 
WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY, 
WindowManager.LayoutParams.FLAG_FULLSCREEN 
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED 
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED, 
PixelFormat.TRANSLUCENT); 
mWindowLayoutParams.setTitle("ScreenshotAnimation"); 
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 
mNotificationManager = 
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 
mDisplay = mWindowManager.getDefaultDisplay(); 
mDisplayMetrics = new DisplayMetrics(); 
mDisplay.getRealMetrics(mDisplayMetrics); 
// Get the various target sizes 
mNotificationIconSize = 
r.getDimensionPixelSize(android.R.dimen.notification_large_icon_height); 
// Scale has to account for both sides of the bg 
mBgPadding = (float) r.getDimensionPixelSize(R.dimen.global_screenshot_bg_padding); 
mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels; 
// Setup the Camera shutter sound 
mCameraSound = new MediaActionSound(); 
mCameraSound.load(MediaActionSound.SHUTTER_CLICK); 
} 
/** 
* Creates a new worker thread and saves the screenshot to the media store. 
*/ 
private void saveScreenshotInWorkerThread(Runnable finisher) { 
SaveImageInBackgroundData data = new SaveImageInBackgroundData(); 
data.context = mContext; 
data.image = mScreenBitmap; 
data.iconSize = mNotificationIconSize; 
data.finisher = finisher; 
new SaveImageInBackgroundTask(mContext, data, mNotificationManager, 
SCREENSHOT_NOTIFICATION_ID).execute(data); 
} 
/** 
* @return the current display rotation in degrees 
*/ 
private float getDegreesForRotation(int value) { 
switch (value) { 
case Surface.ROTATION_90: 
return 360f - 90f; 
case Surface.ROTATION_180: 
return 360f - 180f; 
case Surface.ROTATION_270: 
return 360f - 270f; 
} 
return 0f; 
} 
/** 
* Takes a screenshot of the current display and shows an animation. 
*/ 
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) { 
// We need to orient the screenshot correctly (and the Surface api seems to take screenshots 
// only in the natural orientation of the device :!) 
mDisplay.getRealMetrics(mDisplayMetrics); 
float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels}; 
float degrees = getDegreesForRotation(mDisplay.getRotation()); 
boolean requiresRotation = (degrees > 0); 
if (requiresRotation) { 
// Get the dimensions of the device in its native orientation 
mDisplayMatrix.reset(); 
mDisplayMatrix.preRotate(-degrees); 
mDisplayMatrix.mapPoints(dims); 
dims[0] = Math.abs(dims[0]); 
dims[1] = Math.abs(dims[1]); 
} 
// Take the screenshot 
mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]); 
if (mScreenBitmap == null) { 
notifyScreenshotError(mContext, mNotificationManager); 
finisher.run(); 
return; 
} 
if (requiresRotation) { 
// Rotate the screenshot to the current orientation 
Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels, 
mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888); 
Canvas c = new Canvas(ss); 
c.translate(ss.getWidth() / 2, ss.getHeight() / 2); 
c.rotate(degrees); 
c.translate(-dims[0] / 2, -dims[1] / 2); 
c.drawBitmap(mScreenBitmap, 0, 0, null); 
c.setBitmap(null); 
mScreenBitmap = ss; 
} 
// Optimizations 
mScreenBitmap.setHasAlpha(false); 
mScreenBitmap.prepareToDraw(); 
// Start the post-screenshot animation 
startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, 
statusBarVisible, navBarVisible); 
} 
/** 
* Starts the animation after taking the screenshot 
*/ 
private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible, 
boolean navBarVisible) { 
// Add the view for the animation 
mScreenshotView.setImageBitmap(mScreenBitmap); 
mScreenshotLayout.requestFocus(); 
// Setup the animation with the screenshot just taken 
if (mScreenshotAnimation != null) { 
mScreenshotAnimation.end(); 
} 
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); 
ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation(); 
ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h, 
statusBarVisible, navBarVisible); 
mScreenshotAnimation = new AnimatorSet(); 
mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim); 
mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { 
@Override 
public void onAnimationEnd(Animator animation) { 
// Save the screenshot once we have a bit of time now 
saveScreenshotInWorkerThread(finisher); 
mWindowManager.removeView(mScreenshotLayout); 
} 
}); 
mScreenshotLayout.post(new Runnable() { 
@Override 
public void run() { 
// Play the shutter sound to notify that we've taken a screenshot 
mCameraSound.play(MediaActionSound.SHUTTER_CLICK); 
mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null); 
mScreenshotView.buildLayer(); 
mScreenshotAnimation.start(); 
} 
}); 
} 
private ValueAnimator createScreenshotDropInAnimation() { 
final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION) 
/ SCREENSHOT_DROP_IN_DURATION); 
final float flashDurationPct = 2f * flashPeakDurationPct; 
final Interpolator flashAlphaInterpolator = new Interpolator() { 
@Override 
public float getInterpolation(float x) { 
// Flash the flash view in and out quickly 
if (x <= flashDurationPct) { 
return (float) Math.sin(Math.PI * (x / flashDurationPct)); 
} 
return 0; 
} 
}; 
final Interpolator scaleInterpolator = new Interpolator() { 
@Override 
public float getInterpolation(float x) { 
// We start scaling when the flash is at it's peak 
if (x < flashPeakDurationPct) { 
return 0; 
} 
return (x - flashDurationPct) / (1f - flashDurationPct); 
} 
}; 
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 
anim.setDuration(SCREENSHOT_DROP_IN_DURATION); 
anim.addListener(new AnimatorListenerAdapter() { 
@Override 
public void onAnimationStart(Animator animation) { 
mBackgroundView.setAlpha(0f); 
mBackgroundView.setVisibility(View.VISIBLE); 
mScreenshotView.setAlpha(0f); 
mScreenshotView.setTranslationX(0f); 
mScreenshotView.setTranslationY(0f); 
mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale); 
mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale); 
mScreenshotView.setVisibility(View.VISIBLE); 
mScreenshotFlash.setAlpha(0f); 
mScreenshotFlash.setVisibility(View.VISIBLE); 
} 
@Override 
public void onAnimationEnd(android.animation.Animator animation) { 
mScreenshotFlash.setVisibility(View.GONE); 
} 
}); 
anim.addUpdateListener(new AnimatorUpdateListener() { 
@Override 
public void onAnimationUpdate(ValueAnimator animation) { 
float t = (Float) animation.getAnimatedValue(); 
float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale) 
- scaleInterpolator.getInterpolation(t) 
* (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE); 
mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA); 
mScreenshotView.setAlpha(t); 
mScreenshotView.setScaleX(scaleT); 
mScreenshotView.setScaleY(scaleT); 
mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t)); 
} 
}); 
return anim; 
} 
private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible, 
boolean navBarVisible) { 
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 
anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY); 
anim.addListener(new AnimatorListenerAdapter() { 
@Override 
public void onAnimationEnd(Animator animation) { 
mBackgroundView.setVisibility(View.GONE); 
mScreenshotView.setVisibility(View.GONE); 
mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null); 
} 
}); 
if (!statusBarVisible || !navBarVisible) { 
// There is no status bar/nav bar, so just fade the screenshot away in place 
anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION); 
anim.addUpdateListener(new AnimatorUpdateListener() { 
@Override 
public void onAnimationUpdate(ValueAnimator animation) { 
float t = (Float) animation.getAnimatedValue(); 
float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale) 
- t * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE); 
mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA); 
mScreenshotView.setAlpha(1f - t); 
mScreenshotView.setScaleX(scaleT); 
mScreenshotView.setScaleY(scaleT); 
} 
}); 
} else { 
// In the case where there is a status bar, animate to the origin of the bar (top-left) 
final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION 
/ SCREENSHOT_DROP_OUT_DURATION; 
final Interpolator scaleInterpolator = new Interpolator() { 
@Override 
public float getInterpolation(float x) { 
if (x < scaleDurationPct) { 
// Decelerate, and scale the input accordingly 
return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f)); 
} 
return 1f; 
} 
}; 
// Determine the bounds of how to scale 
float halfScreenWidth = (w - 2f * mBgPadding) / 2f; 
float halfScreenHeight = (h - 2f * mBgPadding) / 2f; 
final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET; 
final PointF finalPos = new PointF( 
-halfScreenWidth + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth, 
-halfScreenHeight + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight); 
// Animate the screenshot to the status bar 
anim.setDuration(SCREENSHOT_DROP_OUT_DURATION); 
anim.addUpdateListener(new AnimatorUpdateListener() { 
@Override 
public void onAnimationUpdate(ValueAnimator animation) { 
float t = (Float) animation.getAnimatedValue(); 
float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale) 
- scaleInterpolator.getInterpolation(t) 
* (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE); 
mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA); 
mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t)); 
mScreenshotView.setScaleX(scaleT); 
mScreenshotView.setScaleY(scaleT); 
mScreenshotView.setTranslationX(t * finalPos.x); 
mScreenshotView.setTranslationY(t * finalPos.y); 
} 
}); 
} 
return anim; 
} 
static void notifyScreenshotError(Context context, NotificationManager nManager) { 
Resources r = context.getResources(); 
// Clear all existing notification, compose the new notification and show it 
Notification n = new Notification.Builder(context) 
.setTicker(r.getString(R.string.screenshot_failed_title)) 
.setContentTitle(r.getString(R.string.screenshot_failed_title)) 
.setContentText(r.getString(R.string.screenshot_failed_text)) 
.setSmallIcon(R.drawable.stat_notify_image_error) 
.setWhen(System.currentTimeMillis()) 
.setAutoCancel(true) 
.getNotification(); 
nManager.notify(SCREENSHOT_NOTIFICATION_ID, n); 
}

可以從這部分源碼中,去尋找解決辦法。

其中有一個很重要的方法就是

mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);

這個是可以截圖的,但是是隱藏的代碼,不提供外面調用的。

注意需要在AndroidManifest.xml中加入代碼:android:sharedUserId="android.uid.system" 進行後續開發。

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