Come posso fare uno screenshot di un'area selezionata dello schermo del telefono non da alcun programma ma dal codice?
Come posso fare uno screenshot di un'area selezionata dello schermo del telefono non da alcun programma ma dal codice?
Risposte:
Ecco il codice che ha permesso di archiviare il mio screenshot su una scheda SD e di utilizzarlo in seguito per qualsiasi necessità:
Innanzitutto, è necessario aggiungere un'autorizzazione adeguata per salvare il file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
E questo è il codice (in esecuzione in un'attività):
private void takeScreenshot() {
Date now = new Date();
android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss", now);
try {
// image naming and path to include sd card appending name you choose for file
String mPath = Environment.getExternalStorageDirectory().toString() + "/" + now + ".jpg";
// create bitmap screen capture
View v1 = getWindow().getDecorView().getRootView();
v1.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
File imageFile = new File(mPath);
FileOutputStream outputStream = new FileOutputStream(imageFile);
int quality = 100;
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
outputStream.flush();
outputStream.close();
openScreenshot(imageFile);
} catch (Throwable e) {
// Several error may come out with file handling or DOM
e.printStackTrace();
}
}
Ed è così che puoi aprire l'immagine generata di recente:
private void openScreenshot(File imageFile) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.fromFile(imageFile);
intent.setDataAndType(uri, "image/*");
startActivity(intent);
}
Se si desidera utilizzarlo nella vista frammento, utilizzare:
View v1 = getActivity().getWindow().getDecorView().getRootView();
invece di
View v1 = getWindow().getDecorView().getRootView();
sulla funzione takeScreenshot ()
Nota :
Questa soluzione non funziona se la finestra di dialogo contiene una vista di superficie. Per i dettagli, controlla la risposta alla seguente domanda:
Android Cattura schermata di Surface View Mostra la schermata nera
mCurrentUrlMask
deve essere una View
poiché è la classe unica nell'API di Android che ha il getRootView()
metodo. Probabilmente è una vista nell'interfaccia utente.
View v1 = mCurrentUrlMask.getRootView();
ho usato View v1 = getWindow().getDecorView().getRootView();
e funziona per me.
Chiama questo metodo, passando nel ViewGroup più esterno di cui desideri una schermata di:
public Bitmap screenShot(View view) {
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),
view.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
view
? `
getWindow().getDecorView().getRootView()
come vista si ottiene uno screenshot della sola app, non dello schermo.
Nota: funziona solo per il telefono rootato
A livello di programmazione, è possibile eseguire adb shell /system/bin/screencap -p /sdcard/img.png
come di seguito
Process sh = Runtime.getRuntime().exec("su", null,null);
OutputStream os = sh.getOutputStream();
os.write(("/system/bin/screencap -p " + "/sdcard/img.png").getBytes("ASCII"));
os.flush();
os.close();
sh.waitFor();
quindi leggi img.png
come Bitmap
e usa come desideri.
System.err : java.io.IOException: Error running exec(). Command: [su] Working Directory: null Environment: null
, il mio dispositivo è Android 5.0
EDIT: abbi pietà dei downvotes. Era vero nel 2010 quando ho risposto alla domanda.
Tutti i programmi che consentono schermate funzionano solo su telefoni con root.
Per questo metodo non sono richiesti permessi di root o grandi codifiche .
Sulla shell adb usando il comando qui sotto puoi fare uno screenshot.
input keyevent 120
Questo comando non richiede alcun permesso di root, quindi puoi eseguire lo stesso anche dal codice java dell'applicazione Android.
Process process;
process = Runtime.getRuntime().exec("input keyevent 120");
Maggiori informazioni sul codice keyevent in Android vedi http://developer.android.com/reference/android/view/KeyEvent.html
Qui abbiamo usato. KEYCODE_SYSRQ il suo valore è 120 e utilizzato per il tasto Richiesta sistema / Stampa schermo.
Come ha detto CJBS, l'immagine in uscita verrà salvata in / sdcard / Pictures / Screenshot
/sdcard/Pictures/Screenshots
su -c "input keyevent 120"
va bene !!
La risposta di Mualig è molto buona, ma ho avuto lo stesso problema descritto da Ewoks, non sto ottenendo lo sfondo. Quindi a volte è abbastanza buono ea volte ottengo testo nero su sfondo nero (a seconda del tema).
Questa soluzione è fortemente basata sul codice Mualig e sul codice che ho trovato in Robotium. Sto scartando l'uso del disegno cache chiamando direttamente il metodo draw. Prima cercherò di ottenere lo sfondo disegnabile dall'attività corrente per disegnarlo per primo.
// Some constants
final static String SCREENSHOTS_LOCATIONS = Environment.getExternalStorageDirectory().toString() + "/screenshots/";
// Get device dimmensions
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
// Get root view
View view = mCurrentUrlMask.getRootView();
// Create the bitmap to use to draw the screenshot
final Bitmap bitmap = Bitmap.createBitmap(size.x, size.y, Bitmap.Config.ARGB_4444);
final Canvas canvas = new Canvas(bitmap);
// Get current theme to know which background to use
final Activity activity = getCurrentActivity();
final Theme theme = activity.getTheme();
final TypedArray ta = theme
.obtainStyledAttributes(new int[] { android.R.attr.windowBackground });
final int res = ta.getResourceId(0, 0);
final Drawable background = activity.getResources().getDrawable(res);
// Draw background
background.draw(canvas);
// Draw views
view.draw(canvas);
// Save the screenshot to the file system
FileOutputStream fos = null;
try {
final File sddir = new File(SCREENSHOTS_LOCATIONS);
if (!sddir.exists()) {
sddir.mkdirs();
}
fos = new FileOutputStream(SCREENSHOTS_LOCATIONS
+ System.currentTimeMillis() + ".jpg");
if (fos != null) {
if (!bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos)) {
Log.d(LOGTAG, "Compress/Write failed");
}
fos.flush();
fos.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
private void captureScreen() {
View v = getWindow().getDecorView().getRootView();
v.setDrawingCacheEnabled(true);
Bitmap bmp = Bitmap.createBitmap(v.getDrawingCache());
v.setDrawingCacheEnabled(false);
try {
FileOutputStream fos = new FileOutputStream(new File(Environment
.getExternalStorageDirectory().toString(), "SCREEN"
+ System.currentTimeMillis() + ".png"));
bmp.compress(CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Aggiungi l'autorizzazione nel manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Per supportare Marshmallow o versioni precedenti, si prega di aggiungere il codice seguente nel metodo onCreate attività
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},00);
Come riferimento, un modo per catturare lo schermo (e non solo l'attività della tua app) è catturare il framebuffer (device / dev / graphics / fb0). Per fare ciò devi avere i privilegi di root o la tua app deve essere un'app con permessi di firma ("Un'autorizzazione che il sistema concede solo se l'applicazione richiedente è firmata con lo stesso certificato dell'applicazione che ha dichiarato l'autorizzazione") - che è molto improbabile a meno che tu non abbia compilato la tua ROM.
Ogni acquisizione di framebuffer, da un paio di dispositivi che ho testato, conteneva esattamente uno screenshot. La gente ha segnalato che ne contiene di più, immagino che dipenda dalla dimensione del frame / display.
Ho provato a leggere continuamente il framebuffer ma sembra tornare per un numero fisso di byte letti. Nel mio caso si tratta di (3 410 432) byte, che è sufficiente per memorizzare un frame di visualizzazione di 854 * 480 RGBA (3 279 360 byte). Sì, il frame, in binario, emesso da fb0 è RGBA nel mio dispositivo. Molto probabilmente questo dipenderà da dispositivo a dispositivo. Questo sarà importante per te decodificarlo =)
Nel mio dispositivo / dev / graphics / fb0 le autorizzazioni sono in modo che solo root e gli utenti della grafica di gruppo possano leggere l'fb0.
la grafica è un gruppo limitato, quindi probabilmente accederai a fb0 solo con un telefono rootato usando il comando su.
Le app Android hanno l' id utente (uid) = app _ ## e l' id gruppo (guid) = app _ ## .
adb shell ha uid = shell e guid = shell , che ha molte più autorizzazioni di un'app. Puoi effettivamente controllare tali permessi su /system/permissions/platform.xml
Questo significa che sarai in grado di leggere fb0 nella shell adb senza root ma non lo leggerai all'interno dell'app senza root.
Inoltre, concedere le autorizzazioni READ_FRAME_BUFFER e / o ACCESS_SURFACE_FLINGER su AndroidManifest.xml non farà nulla per un'app normale perché funzioneranno solo con app di ' firma '.
Controlla anche questa discussione chiusa per maggiori dettagli.
La mia soluzione è:
public static Bitmap loadBitmapFromView(Context context, View v) {
DisplayMetrics dm = context.getResources().getDisplayMetrics();
v.measure(MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.EXACTLY));
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
Bitmap returnedBitmap = Bitmap.createBitmap(v.getMeasuredWidth(),
v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(returnedBitmap);
v.draw(c);
return returnedBitmap;
}
e
public void takeScreen() {
Bitmap bitmap = ImageUtils.loadBitmapFromView(this, view); //get Bitmap from the view
String mPath = Environment.getExternalStorageDirectory() + File.separator + "screen_" + System.currentTimeMillis() + ".jpeg";
File imageFile = new File(mPath);
OutputStream fout = null;
try {
fout = new FileOutputStream(imageFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fout);
fout.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
fout.close();
}
}
Le immagini vengono salvate nella cartella di archiviazione esterna.
view
stai passando ImageUtils.loadBitmapFromView(this, view)
?
Puoi provare la seguente libreria: http://code.google.com/p/android-screenshot-library/ Android Screenshot Library (ASL) consente di acquisire a livello di codice schermate da dispositivi Android senza la necessità di disporre dei privilegi di accesso root. Invece, ASL utilizza un servizio nativo in esecuzione in background, avviato tramite Android Debug Bridge (ADB) una volta all'avvio del dispositivo.
Sulla base della risposta di @JustinMorris sopra e @NiravDangi qui https://stackoverflow.com/a/8504958/2232148 dobbiamo prendere lo sfondo e il primo piano di una vista e assemblarli in questo modo:
public static Bitmap takeScreenshot(View view, Bitmap.Config quality) {
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), quality);
Canvas canvas = new Canvas(bitmap);
Drawable backgroundDrawable = view.getBackground();
if (backgroundDrawable != null) {
backgroundDrawable.draw(canvas);
} else {
canvas.drawColor(Color.WHITE);
}
view.draw(canvas);
return bitmap;
}
Il parametro quality accetta una costante di Bitmap.Config, in genere o Bitmap.Config.RGB_565
o Bitmap.Config.ARGB_8888
.
public class ScreenShotActivity extends Activity{
private RelativeLayout relativeLayout;
private Bitmap myBitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
relativeLayout = (RelativeLayout)findViewById(R.id.relative1);
relativeLayout.post(new Runnable() {
public void run() {
//take screenshot
myBitmap = captureScreen(relativeLayout);
Toast.makeText(getApplicationContext(), "Screenshot captured..!", Toast.LENGTH_LONG).show();
try {
if(myBitmap!=null){
//save image to SD card
saveImage(myBitmap);
}
Toast.makeText(getApplicationContext(), "Screenshot saved..!", Toast.LENGTH_LONG).show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
public static Bitmap captureScreen(View v) {
Bitmap screenshot = null;
try {
if(v!=null) {
screenshot = Bitmap.createBitmap(v.getMeasuredWidth(),v.getMeasuredHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(screenshot);
v.draw(canvas);
}
}catch (Exception e){
Log.d("ScreenShotActivity", "Failed to capture screenshot because:" + e.getMessage());
}
return screenshot;
}
public static void saveImage(Bitmap bitmap) throws IOException{
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 40, bytes);
File f = new File(Environment.getExternalStorageDirectory() + File.separator + "test.png");
f.createNewFile();
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
fo.close();
}
}
AGGIUNGI PERMESSO
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Puoi provare a fare qualcosa del genere,
Ottenere una cache bitmap da un layout o una vista facendo qualcosa come First Devi setDrawingCacheEnabled
andare a un layout (un linearlayout o relativelayout o una vista)
poi
Bitmap bm = layout.getDrawingCache()
Quindi fai quello che vuoi con la bitmap. O trasformandolo in un file immagine, o invia l'uri della bitmap da qualche altra parte.
Ho creato una semplice libreria che prende uno screenshot da un View
e ti dà un oggetto Bitmap o lo salva direttamente in qualsiasi percorso tu voglia
Screenshot.takeScreenshot(view, mPath); imageView.setImageBitmap(Screenshot.getBitmapScreenshot(view, mPath));
hai menzionato Come visualizzare l'attività corrente. Non voglio usare l'id dell'xml.
takeScreenshotForScreen()
invece.
La via breve è
FrameLayout layDraw = (FrameLayout) findViewById(R.id.layDraw); /*Your root view to be part of screenshot*/
layDraw.buildDrawingCache();
Bitmap bmp = layDraw.getDrawingCache();
Sto solo estendendo la risposta di Taraloca. È necessario aggiungere le seguenti righe per farlo funzionare. Ho reso statico il nome dell'immagine. Assicurati di utilizzare la variabile timestamp di taraloca in caso tu abbia bisogno di un nome immagine dinamico.
// Storage Permissions
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private void verifyStoragePermissions() {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}else{
takeScreenshot();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (requestCode == REQUEST_EXTERNAL_STORAGE) {
takeScreenshot();
}
}
}
E nel file AndroidManifest.xml sono necessarie le seguenti voci:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Se vuoi fare uno screenshot da allora fragment
segui questo:
Ignora onCreateView()
:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_one, container, false);
mView = view;
}
Logica per fare screenshot:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
View view = mView.findViewById(R.id.scrollView1);
shareScreenShotM(view, (NestedScrollView) view);
}
metodo shareScreenShotM)()
:
public void shareScreenShotM(View view, NestedScrollView scrollView){
bm = takeScreenShot(view,scrollView); //method to take screenshot
File file = savePic(bm); // method to save screenshot in phone.
}
metodo takeScreenShot ():
public Bitmap takeScreenShot(View u, NestedScrollView z){
u.setDrawingCacheEnabled(true);
int totalHeight = z.getChildAt(0).getHeight();
int totalWidth = z.getChildAt(0).getWidth();
Log.d("yoheight",""+ totalHeight);
Log.d("yowidth",""+ totalWidth);
u.layout(0, 0, totalWidth, totalHeight);
u.buildDrawingCache();
Bitmap b = Bitmap.createBitmap(u.getDrawingCache());
u.setDrawingCacheEnabled(false);
u.destroyDrawingCache();
return b;
}
metodo savePic ():
public static File savePic(Bitmap bm){
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
File sdCardDirectory = new File(Environment.getExternalStorageDirectory() + "/Foldername");
if (!sdCardDirectory.exists()) {
sdCardDirectory.mkdirs();
}
// File file = new File(dir, fileName);
try {
file = new File(sdCardDirectory, Calendar.getInstance()
.getTimeInMillis() + ".jpg");
file.createNewFile();
new FileOutputStream(file).write(bytes.toByteArray());
Log.d("Fabsolute", "File Saved::--->" + file.getAbsolutePath());
Log.d("Sabsolute", "File Saved::--->" + sdCardDirectory.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
Per l'attività puoi semplicemente usare View v1 = getWindow().getDecorView().getRootView();
invece dimView
La maggior parte delle risposte a questa domanda utilizza il Canvas
metodo di disegno o il metodo cache di disegno. Tuttavia, il View.setDrawingCache()
metodo è obsoleto nell'API 28 . Attualmente l'API consigliata per creare schermate è la PixelCopy
classe disponibile da API 24 (ma i metodi che accettano i Window
parametri sono disponibili da API 26 == Android 8.0 Oreo). Ecco un esempio di codice Kotlin per il recupero di un Bitmap
:
@RequiresApi(Build.VERSION_CODES.O)
fun saveScreenshot(view: View) {
val window = (view.context as Activity).window
if (window != null) {
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
val locationOfViewInWindow = IntArray(2)
view.getLocationInWindow(locationOfViewInWindow)
try {
PixelCopy.request(window, Rect(locationOfViewInWindow[0], locationOfViewInWindow[1], locationOfViewInWindow[0] + view.width, locationOfViewInWindow[1] + view.height), bitmap, { copyResult ->
if (copyResult == PixelCopy.SUCCESS) {
saveBitmap(bitmap)
}
// possible to handle other result codes ...
}, Handler())
} catch (e: IllegalArgumentException) {
// PixelCopy may throw IllegalArgumentException, make sure to handle it
}
}
}
La vista parametri è l'oggetto di layout principale.
public static Bitmap screenShot(View view) {
Bitmap bitmap = null;
if (view.getWidth() > 0 && view.getHeight() > 0) {
bitmap = Bitmap.createBitmap(view.getWidth(),
view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
}
return bitmap;
}
Schermata di scorrimento a pagina intera
Se vuoi catturare uno screenshot di visualizzazione completa (che contiene una vista di scorrimento o giù di lì), controlla in questa libreria
Tutto quello che devi fare è importare Gradel e creare un oggetto di BigScreenshot
BigScreenshot longScreenshot = new BigScreenshot(this, x, y);
Verrà ricevuto un callback con la bitmap degli screenshot acquisiti mentre si scorre automaticamente il gruppo di visualizzazione dello schermo e alla fine assemblati insieme.
@Override public void getScreenshot(Bitmap bitmap) {}
Che possono essere salvati nella galleria o qualsiasi utilizzo è necessario il loro dopo
Cattura uno screenshot di una vista in Android.
public static Bitmap getViewBitmap(View v) {
v.clearFocus();
v.setPressed(false);
boolean willNotCache = v.willNotCacheDrawing();
v.setWillNotCacheDrawing(false);
int color = v.getDrawingCacheBackgroundColor();
v.setDrawingCacheBackgroundColor(0);
if (color != 0) {
v.destroyDrawingCache();
}
v.buildDrawingCache();
Bitmap cacheBitmap = v.getDrawingCache();
if (cacheBitmap == null) {
return null;
}
Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
v.destroyDrawingCache();
v.setWillNotCacheDrawing(willNotCache);
v.setDrawingCacheBackgroundColor(color);
return bitmap;
}
se vuoi catturare una vista o un layout come RelativeLayout o LinearLayout ecc. usa semplicemente il codice:
LinearLayout llMain = (LinearLayout) findViewById(R.id.linearlayoutMain);
Bitmap bm = loadBitmapFromView(llMain);
ora puoi salvare questa bitmap sulla memoria del dispositivo:
FileOutputStream outStream = null;
File f=new File(Environment.getExternalStorageDirectory()+"/Screen Shots/");
f.mkdir();
String extStorageDirectory = f.toString();
File file = new File(extStorageDirectory, "my new screen shot");
pathOfImage = file.getAbsolutePath();
try {
outStream = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.PNG, 100, outStream);
Toast.makeText(getApplicationContext(), "Saved at "+f.getAbsolutePath(), Toast.LENGTH_LONG).show();
addImageGallery(file);
//mail.setEnabled(true);
flag=true;
} catch (FileNotFoundException e) {e.printStackTrace();}
try {
outStream.flush();
outStream.close();
} catch (IOException e) {e.printStackTrace();}