วิธีการถ่ายภาพหน้าจอบน Android โดยทางโปรแกรม


495

ฉันจะถ่ายภาพหน้าจอของพื้นที่ที่เลือกไว้ของหน้าจอโทรศัพท์ไม่ใช่โดยโปรแกรมใด ๆ แต่จากรหัสได้อย่างไร


4
ไม่ได้มาจากโปรแกรมจำลอง ฉันต้องทำสกรีนช็อตของส่วนของ programm ของฉันและทำ smth ใน programm เดียวกัน
korovaisdead

1
อีกแหล่งอ้างอิงที่ดี: stackoverflow.com/a/10296881/439171
Italo Borssatto

นี่คือวิธีที่แฮ็กเกอร์รหัส.com/android
ฮาร์ด

คำตอบ:


448

นี่คือรหัสที่อนุญาตให้ภาพหน้าจอของฉันถูกเก็บไว้ในการ์ด SD และใช้ในภายหลังสำหรับสิ่งที่คุณต้องการคือ:

ก่อนอื่นคุณต้องเพิ่มสิทธิ์ที่เหมาะสมในการบันทึกไฟล์:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

และนี่คือรหัส (ทำงานในกิจกรรม):

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();
    }
}

และนี่คือวิธีที่คุณสามารถเปิดภาพที่เพิ่งสร้างขึ้น:

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);
}

หากคุณต้องการใช้สิ่งนี้ในมุมมองชิ้นส่วนให้ใช้:

View v1 = getActivity().getWindow().getDecorView().getRootView();

แทน

View v1 = getWindow().getDecorView().getRootView();

ในฟังก์ชั่นtakeSc แยง ()

หมายเหตุ :

โซลูชันนี้ใช้ไม่ได้หากกล่องโต้ตอบของคุณมีมุมมองพื้นผิว สำหรับรายละเอียดโปรดตรวจสอบคำตอบสำหรับคำถามต่อไปนี้:

Android ถ่ายภาพหน้าจอของ Surface View แสดงหน้าจอสีดำ


23
สวัสดีคุณช่วยอธิบายว่า mCurrentUrlMask คืออะไร ฉันได้ลองใช้รหัสนี้แล้ว แต่ให้ NullPointerException ที่ Bitmap.createBitmap (v1.getDrawingCache ()) เสมอใครบอกได้ไหมว่าฉันทำอะไรผิด? ความช่วยเหลือใด ๆ ที่ชื่นชม ขอบคุณ
Mitesh Sardhara

4
@MiteshSardhara mCurrentUrlMaskจะต้องViewเป็นคลาสที่ไม่เหมือนใครใน Android API ที่มีgetRootView()เมธอด อาจเป็นมุมมองใน UI
gipi

6
คุณช่วยบอกฉันหน่อยได้ไหมว่า mCurrentUrlMask คืออะไร
Jayeshkumar Sojitra

37
อินสแตนซ์ของView v1 = mCurrentUrlMask.getRootView();ฉันใช้View v1 = getWindow().getDecorView().getRootView();แล้วและได้ผลกับฉัน
enadun

9
คำตอบนี้ใช้ภาพหน้าจอของแอปไม่ใช่แค่ "หน้าจอโทรศัพท์" ตามที่ถามในคำถาม - หรือฉันกำลังทำอะไรผิด
AlikElzin-kilaka

126

เรียกวิธีนี้โดยส่งผ่าน ViewGroup ส่วนใหญ่ด้านนอกที่คุณต้องการภาพหน้าจอของ:

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;
}

9
นี่น่าจะเป็นโค้ดที่สะอาดกว่าคำตอบที่ยอมรับ มันทำงานได้ดีเช่นกัน?
jophde

2
ฉันเคยใช้มาระยะหนึ่งแล้วในแอพที่แตกต่างกันเล็กน้อยและยังไม่มีปัญหาใด ๆ
JustinMorris

3
หากคุณต้องการได้ภาพหน้าจอของโทรศัพท์ที่แอพของคุณอยู่ด้านหลังคุณจะผ่านviewอะไรไปบ้าง "
AlikElzin-kilaka

2
การส่งผ่านgetWindow().getDecorView().getRootView()เป็นมุมมองจะส่งผลให้มีภาพหน้าจอของแอปไม่ใช่แค่หน้าจอ
AlikElzin-kilaka

คำตอบนี้ใช้ภาพหน้าจอของแอปไม่ใช่แค่ "หน้าจอโทรศัพท์" ตามที่ถามในคำถาม - หรือฉันกำลังทำอะไรผิด
AlikElzin-kilaka

42

หมายเหตุ: ใช้งานได้กับโทรศัพท์ที่รูทเท่านั้น

โดยทางโปรแกรมคุณสามารถเรียกใช้adb shell /system/bin/screencap -p /sdcard/img.pngด้านล่าง

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();    

แล้วอ่านimg.pngเป็นBitmapและใช้เป็นที่ต้องการของคุณ


มันจำเป็นต้องเข้าถึงรูทหรือไม่?
ซูม

3
ใช่คุณต้องการการเข้าถึงรูท โปรดตรวจสอบหมายเหตุที่ส่วนหัว
Viswanath Lekshmanan

สวัสดีฉันได้รับSystem.err : java.io.IOException: Error running exec(). Command: [su] Working Directory: null Environment: null อุปกรณ์ของฉันคือ Android 5.0
Eddy

@Eddy มันขึ้นอยู่กับอุปกรณ์ของคุณ
Demian

มีวิธีอ่านเอาต์พุตโดยตรงโดยไม่บันทึกผลลัพธ์ลงในไฟล์จากนั้นอ่านอีกครั้งหรือไม่? stackoverflow.com/questions/43914035
Yazan W Yusuf

35

แก้ไข: มีความเมตตากับ downvotes มันเป็นเรื่องจริงในปี 2010 เมื่อฉันตอบคำถาม

โปรแกรมทั้งหมดที่อนุญาตให้ภาพหน้าจอทำงานบนโทรศัพท์ที่รูทเครื่องเท่านั้น


ดูเหมือนว่าทุกวันนี้เป็นไปได้บน Android ที่มีชิปเซ็ตที่ใช้ Tegra
GDR

4.0.3 - แล้วอาจเป็นหนึ่งในแท็บเล็ต tegra โรงเรียนใหม่เหล่านี้
GDR

AFAIK เมื่ออุปกรณ์มีแอพที่เก็บสต็อกเพื่อจับภาพหน้าจอมันเป็นแอพที่มีสิทธิ์ลายเซ็นพิเศษที่อนุญาตให้จับเฟรม framebuffer (บัฟเฟอร์ที่มีพิกเซลหน้าจอ) ดังนั้นหากรอมของคุณมีแอพดังกล่าวคุณจะไม่จำเป็นต้องทำการรูท แต่แอพนั้นสามารถทำการจับภาพไม่ใช่ของคุณ
Rui Marques

2
พูดอย่างเคร่งครัดพร้อมกับบัญชีผู้ใช้ "shell" ของรูท ADB จะได้รับอนุญาตให้ทำเช่นนี้เนื่องจากเป็นกรณีการใช้งานที่ตั้งใจ สิ่งที่ไม่สามารถหาได้ในอดีต (ยกเว้นการสร้างเป็นครั้งคราวเป็นข้อผิดพลาด) เป็นวิธีที่id ผู้ใช้ของแอปพลิเคชันต้องทำ
Chris Stratton

26

ไม่ต้องขออนุญาตรูทหรือไม่ต้องใช้โค้ดขนาดใหญ่สำหรับวิธีนี้


ใน adb shell โดยใช้คำสั่งด้านล่างคุณสามารถถ่ายภาพหน้าจอได้

input keyevent 120

คำสั่งนี้ไม่จำเป็นต้องได้รับอนุญาตจากรูทใด ๆ ดังนั้นคุณสามารถทำได้จากจาวาโค้ดของแอปพลิเคชัน Android

Process process;
process = Runtime.getRuntime().exec("input keyevent 120");

เพิ่มเติมเกี่ยวกับรหัส keyevent ใน Android ดูhttp://developer.android.com/reference/android/view/KeyEvent.html

ที่นี่เราได้ใช้ KEYCODE_SYSRQค่าของมันคือ 120 และใช้สำหรับคีย์คำขอของระบบ / พิมพ์หน้าจอ


ในฐานะที่เป็น CJBS กล่าวว่าภาพที่ส่งออกจะถูกบันทึกไว้ใน/ sdcard / รูปภาพ / ภาพหน้าจอ


รูปภาพผลลัพธ์จะถูกบันทึกไว้ที่นี่:/sdcard/Pictures/Screenshots
CJBS

ต้องใช้สิทธิ์รูทหรือไม่?
Taranmeet Singh

1
มันจะทำงานบนขนมหวาน? หรือมันจะทำงานในพื้นหลังบริการ @JeegarPatel
karanatwal.github.io

2
ไม่ทำงานโดยทางโปรแกรม แต่ทำงานจากเชลล์ พยายามกับตังเม
mehmet6parmak

1
su -c "input keyevent 120"ก็โอเค !!
ipcjs

17

คำตอบของ Mualig นั้นดีมาก แต่ฉันมีปัญหาแบบเดียวกันที่ Ewoks อธิบายว่าฉันไม่ได้พื้นหลัง ดังนั้นบางครั้งก็ดีพอและบางครั้งฉันได้รับข้อความสีดำบนพื้นหลังสีดำ (ขึ้นอยู่กับธีม)

โซลูชันนี้มีพื้นฐานอย่างมากในรหัส Mualig และรหัสที่ฉันพบใน Robotium ฉันกำลังยกเลิกการใช้แคชการวาดโดยเรียกวิธีการวาดโดยตรง ก่อนหน้านั้นฉันจะพยายามดึงพื้นหลังออกจากกิจกรรมปัจจุบันเพื่อวาดมันก่อน

// 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();
}

ฉันมี listview พร้อม android: cacheColorHint = "# 00000000" ในกิจกรรมของฉันและฉันใช้รหัสนี้เพื่อจับภาพหน้าจอฉันยังมีพื้นหลังสีดำเพราะฉันพบว่าพื้นหลังที่ได้รับจากชุดรูปแบบเป็นสีดำฉันจะจัดการกับสิ่งนี้ได้อย่างไร ListView?
Wangchao0721

กิจกรรมกิจกรรม = getCurrentActivity ให้วิธี Eroor ไม่ได้กำหนด
Shabbir Dhangot

17
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();
    }
}

เพิ่มการอนุญาตในไฟล์ Manifest

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

สำหรับการสนับสนุนMarshmallowหรือรุ่นที่สูงกว่าโปรดเพิ่มโค้ดด้านล่างในกิจกรรมบนวิธีการสร้าง

ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},00);

ต้องใช้: <ใช้สิทธิ์อนุญาต android: name = "android.permission.WRITE_EXTERNAL_STORAGE" />
AndroidGuy

4
คำตอบนี้ใช้ภาพหน้าจอของแอปไม่ใช่เป็น "หน้าจอโทรศัพท์" ตามที่ถามในคำถาม - หรือฉันกำลังทำอะไรผิดหรือเปล่า?
AlikElzin-kilaka

ถ้าฉันต้องการภาพหน้าจอของรายการมุมมองล่ะ
Anshul Tyagi

ฉันต้องการที่จะจับภาพจากอะแดปเตอร์ความคิดใด ๆ ที่ฉันจะทำมันได้อย่างไร
Deep Dave

16

เป็นการอ้างอิงวิธีหนึ่งในการจับภาพหน้าจอ (และไม่ใช่แค่กิจกรรมแอพของคุณ) คือการจับframebuffer (อุปกรณ์ / dev / graphics / fb0) ในการทำเช่นนี้คุณต้องมีสิทธิ์ใช้งานรูทหรือแอพของคุณต้องเป็นแอพที่มีสิทธิ์อนุญาตลายเซ็น ("การอนุญาตที่ระบบให้สิทธิ์เฉพาะเมื่อแอปพลิเคชันที่ร้องขอมีการเซ็นชื่อด้วยใบรับรองเดียวกับแอปพลิเคชัน ไม่น่าเป็นไปได้มากถ้าคุณไม่ได้รวบรวม ROM ของคุณเอง

แต่ละจับ framebuffer จากคู่ของอุปกรณ์ฉันมีการทดสอบที่มีอยู่ว่าหนึ่งหน้าจอ ผู้คนรายงานว่ามีมากกว่าฉันคิดว่ามันขึ้นอยู่กับขนาดของเฟรม / ดิสเพลย์

ฉันพยายามอ่าน framebuffer อย่างต่อเนื่อง แต่ดูเหมือนว่าจะกลับมาเป็นจำนวนคงที่อ่านไบต์ ในกรณีของฉันนั่นคือ (3 410 432) ไบต์ซึ่งเพียงพอที่จะเก็บเฟรมแสดงผลของ 854 * 480 RGBA (3 279 360 ไบต์) ใช่เฟรมเป็นไบนารี่เอาต์พุตจาก fb0 คือRGBAในอุปกรณ์ของฉัน สิ่งนี้จะขึ้นอยู่กับอุปกรณ์ นี่จะเป็นสิ่งสำคัญสำหรับคุณที่จะถอดรหัส =)

ในสิทธิ์ใช้งานอุปกรณ์/ dev / graphics / fb0 ของฉันคือเพื่อให้เฉพาะผู้ใช้จาก root และกราฟิกกลุ่มสามารถอ่าน fb0

graphicsเป็นกลุ่มที่ถูก จำกัด ดังนั้นคุณอาจเข้าถึง fb0 ด้วยโทรศัพท์รูทโดยใช้คำสั่ง su

ปพลิเคชัน Android มีรหัสผู้ใช้ (UID) = แอป _ ##และกลุ่ม ID (GUID) = แอป

adb shellมีuid = shellและguid = shellซึ่งมีสิทธิ์มากกว่าแอป คุณสามารถตรวจสอบการอนุญาตเหล่านั้นได้ที่ /system/permissions/platform.xml

ซึ่งหมายความว่าคุณจะสามารถอ่าน fb0 ในเชลล์ adb ที่ไม่มีรูท แต่คุณจะไม่อ่านภายในแอปที่ไม่มีรูท

นอกจากนี้การให้สิทธิ์ READ_FRAME_BUFFER และ / หรือ ACCESS_SURFACE_FLINGER บน AndroidManifest.xml จะไม่ทำอะไรเลยสำหรับแอปทั่วไปเพราะสิ่งเหล่านี้จะใช้ได้กับแอป ' ลายเซ็น ' เท่านั้น

ตรวจสอบหัวข้อนี้ปิดเพื่อดูรายละเอียดเพิ่มเติม


2
ใช้งานได้ (ใช้งานได้หรือไม่) ในโทรศัพท์บางรุ่น แต่โปรดทราบว่าโทรศัพท์ที่ใช้ GPU ไม่จำเป็นต้องให้เฟรมเฟรมเชิงเส้นแก่โปรเซสเซอร์แอปพลิเคชัน
Chris Stratton

15

ทางออกของฉันคือ

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;
}

และ

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();
    }
}

ภาพจะถูกบันทึกในโฟลเดอร์จัดเก็บข้อมูลภายนอก


1
คุณควรปิด FileOutputStream ในที่สุดบล็อก หากคุณพบข้อยกเว้นตอนนี้สตรีมจะไม่ถูกปิด
Wotuu

สิ่งที่viewคุณกำลังผ่านไปImageUtils.loadBitmapFromView(this, view)?
AlikElzin-kilaka

1
ทำไมคุณใช้ v.layout (0, 0, v.getMeasuredWidth (), v.getMeasuredHeight ()); ? นั่นไม่เปลี่ยนมุมมอง (v)
serj

11

คุณสามารถลองใช้ห้องสมุดดังต่อไปนี้: http://code.google.com/p/android-sc4.0-library/ ห้องสมุดภาพหน้าจอ Android (ASL) ช่วยให้สามารถจับภาพหน้าจอจากอุปกรณ์ Android โดยทางโปรแกรมโดยไม่ต้องมีสิทธิ์การเข้าถึงรูต ASL จะใช้บริการเนทีฟที่ทำงานอยู่เบื้องหลังโดยเริ่มต้นจาก Android Debug Bridge (ADB) หนึ่งครั้งต่อการบูตอุปกรณ์


1
ฉันลองสิ่งนี้ แต่ใช้ภาพหน้าจอของแอปที่รันอยู่คนเดียว มันช่วยไม่ได้ถ้าฉันต้องการถ่ายภาพหน้าจอของหน้าจอหลัก คุณจะถ่ายภาพหน้าจอของหน้าจอหลักด้วยรหัสได้อย่างไร?
Jana

1
@ Janardhanan.S: นี่คือคำถามที่ถาม คุณช่วยอธิบายเพิ่มเติมด้วยคำตอบใหม่แทนที่จะถามคำถามแยกต่างหาก
trgraglia

3
ปัญหาเดียวกันกับฉัน .. "Native Service Not Running !!"
Yogesh Maheshwari

1
เหมือนกันกับฉัน "Native Service Not Running !!" .... ใครสามารถเพิ่มข้อความช่วยเหลือที่นี่ได้ไหม?
Pankaj Kumar

1
กันที่นี่! "บริการแบบเนทีฟไม่ทำงาน !!" และในเวอร์ชันล่าสุด 1.2 พวกเขาใช้คลาส API LEVEL 19 เช่น Socket เป็นต้น
user347187

10

ตามคำตอบของ @JustinMorris ด้านบนและ @NiravDangi ที่นี่https://stackoverflow.com/a/8504958/2232148เราจะต้องใช้พื้นหลังและพื้นหน้าของมุมมองและรวบรวมพวกเขาเช่นนี้:

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;
}

พารามิเตอร์ที่มีคุณภาพจะใช้เวลาอย่างต่อเนื่องของ Bitmap.Config โดยทั่วไปอย่างใดอย่างหนึ่งหรือBitmap.Config.RGB_565Bitmap.Config.ARGB_8888


การวาดผืนผ้าใบด้วยสีขาวในกรณีที่พื้นหลังเป็นโมฆะ ขอบคุณ Oliver
Shaleen

8
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();
}

}

เพิ่มสิทธิ์

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

คำตอบนี้ใช้ภาพหน้าจอของแอปไม่ใช่เป็น "หน้าจอโทรศัพท์" ตามที่ถามในคำถาม - หรือฉันกำลังทำอะไรผิดหรือเปล่า?
AlikElzin-kilaka

7

คุณสามารถลองทำอะไรเช่นนี้

รับแคชบิตแมปจากเลย์เอาต์หรือมุมมองโดยทำอะไรเช่นแรกที่คุณต้องทำ setDrawingCacheEnabledวางเลย์เอาต์ (linearlayout หรือ relativelayayout หรือมุมมอง)

แล้วก็

Bitmap bm = layout.getDrawingCache()

จากนั้นคุณทำสิ่งที่คุณต้องการด้วยบิตแมป เปลี่ยนเป็นไฟล์รูปภาพหรือส่ง uri ของบิตแมปไปที่อื่น


1
นี่เป็นวิธีที่ดีที่สุดหากเป็นแอพที่คุณเขียนไปยังบิตแมป นอกจากนี้ระวังวิธีการที่มีความล่าช้าก่อนที่จะแคช ... เช่นแจ้งเตือน ... () สำหรับมุมมองรายการ
trgraglia

ไม่ใช่แค่นั้น เลย์เอาต์ต้องแสดงบนหน้าจอก่อน มันคือ "แคช" ที่คุณได้รับหลังจากนี้ พยายามซ่อนมุมมองและถ่ายภาพหน้าจอเป็นพื้นหลัง (ตามทฤษฎี) แต่ไม่ได้ผล
Kevin Tan

มีประสิทธิภาพมากกว่าโซลูชันอื่น ๆ
sivi

6

สำหรับผู้ที่ต้องการจับภาพ GLSurfaceView เมธอด getDrawingCache หรือการวาดเป็นผืนผ้าใบจะไม่ทำงาน

คุณต้องอ่านเนื้อหาของเฟรมอลเฟรม OpenGL หลังจากเฟรมเรนเดอร์แล้ว มีคำตอบที่ดีอยู่ที่นี่


6

ฉันได้สร้างห้องสมุดง่าย ๆ ที่ใช้สกรีนช็อตจาก Viewและให้วัตถุ Bitmap กับคุณหรือบันทึกลงในเส้นทางใด ๆ ที่คุณต้องการโดยตรง

https://github.com/abdallahalaraby/Blink


มันต้องใช้โทรศัพท์รูทหรือไม่?
Nilesh Agrawal

1
ไม่ต้องทำการรูท :)
Abdallah Alaraby

ในการใช้งานคุณกล่าวถึงScreenshot.takeScreenshot(view, mPath); imageView.setImageBitmap(Screenshot.getBitmapScreenshot(view, mPath)); วิธีดูภาพกิจกรรมปัจจุบัน ฉันไม่ต้องการใช้รหัสของ xml
Nilesh Agrawal

ใช้takeScreenshotForScreen()แทน
Abdallah Alaraby


5

เพียงแค่ขยายคำตอบของ taraloca คุณต้องเพิ่มบรรทัดต่อไปนี้เพื่อให้ทำงานได้ ฉันทำให้ชื่อรูปภาพคงที่ โปรดให้แน่ใจว่าคุณใช้ตัวแปรเวลาของ taraloca ในกรณีที่คุณต้องการชื่อภาพแบบไดนามิก

    // 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();
        }
    }
}

และในไฟล์ AndroidManifest.xml รายการต่อไปนี้จะต้อง:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

4

หากคุณต้องการถ่ายภาพหน้าจอจากด้านล่างให้fragment ทำตามนี้:

  1. แทนที่ 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;
            }
  2. ตรรกะสำหรับการถ่ายภาพหน้าจอ:

     button.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View v) {
         View view =  mView.findViewById(R.id.scrollView1);
          shareScreenShotM(view, (NestedScrollView) view); 
     }
  3. วิธีการ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.
        }
  4. วิธีการ 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;
            }
  5. วิธีการ 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;
       }

สำหรับกิจกรรมที่คุณสามารถใช้View v1 = getWindow().getDecorView().getRootView();แทนmView


3

คำตอบส่วนใหญ่สำหรับคำถามนี้ใช้Canvasวิธีการวาดหรือวิธีการแคชการวาด อย่างไรก็ตามView.setDrawingCache()วิธีการจะเลิกใน API 28 ปัจจุบัน API ที่แนะนำสำหรับการทำภาพหน้าจอเป็นPixelCopyคลาสที่มีให้บริการจาก API 24 (แต่วิธีการที่รับWindowพารามิเตอร์มีให้บริการจาก API 26 == Android 8.0 Oreo) นี่คือตัวอย่างโค้ด Kotlin สำหรับการดึง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
        }
    }
}

ปัญหาเกี่ยวกับ PixelCopy เหนือวิธีแก้ปัญหา drawingCache นั้นดูเหมือนว่าจะถ่ายได้เฉพาะพิกเซลที่มองเห็นได้เท่านั้น ส่วนที่เหลือเป็นสีดำสำหรับฉัน ดังนั้นจึงเป็นไปไม่ได้ที่จะแบ่งปันสิ่งที่สามารถเลื่อนได้และอยู่นอกกรอบ คุณมีทางออกสำหรับสิ่งนี้หรือไม่?
Henning

คุณพูดถูก ในทำนองเดียวกันอาจทำให้เกิดปัญหาได้หากมุมมองอื่นแสดงขึ้นเหนือมุมมองที่ฉันต้องการจับภาพหน้าจอ (เช่นเมื่อเปิดลิ้นชัก) ในกรณีเหล่านี้ฉันใช้วิธี Canvas แบบเก่า :(
MilošČernilovský

1
เป็นไปได้ไหมที่จะจับภาพหน้าจอของแอพที่กำลังรันด้วย PixelCopy
Amir Rezaei

2

สำหรับแอปพลิเคชันระบบเท่านั้น!

Process process;
process = Runtime.getRuntime().exec("screencap -p " + outputPath);
process.waitFor();

หมายเหตุ: แอปพลิเคชันระบบไม่จำเป็นต้องเรียกใช้ "su" เพื่อดำเนินการคำสั่งนี้


2

มุมมองพารามิเตอร์คือวัตถุรูปแบบรูท

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;
                }

2

สำหรับภาพหน้าจอเลื่อนเต็มหน้า

หากคุณต้องการจับภาพหน้าจอมุมมองแบบเต็ม (ซึ่งมี scrollview หรือมากกว่านั้น) ให้ตรวจสอบที่ห้องสมุดนี้

https://github.com/peter1492/LongScreenshot

สิ่งที่คุณต้องทำคือนำเข้า Gradel และสร้างวัตถุของ BigSc โอเวอร์

BigScreenshot longScreenshot = new BigScreenshot(this, x, y);

จะได้รับการเรียกกลับด้วยบิตแมปของภาพหน้าจอที่ถ่ายในขณะที่เลื่อนดูกลุ่มมุมมองหน้าจอโดยอัตโนมัติและท้ายที่สุดจะรวมเข้าด้วยกัน

@Override public void getScreenshot(Bitmap bitmap) {}

ซึ่งสามารถบันทึกลงในแกลเลอรี่หรือการใช้งานใด ๆ ที่จำเป็นหลังจากนั้น


1

จับภาพหน้าจอของมุมมองใน 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;
}

-1

หากคุณต้องการจับภาพมุมมองหรือเค้าโครงเช่น RelativeLayout หรือ LinearLayout เป็นต้นเพียงใช้รหัส:

LinearLayout llMain = (LinearLayout) findViewById(R.id.linearlayoutMain);
Bitmap bm = loadBitmapFromView(llMain);

ตอนนี้คุณสามารถบันทึกบิตแมปนี้ในที่เก็บข้อมูลอุปกรณ์โดย:

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();}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.