Android ImageView長按保存圖片及截屏相關知識

在日常開發中,可能會需要做長按保存圖片這個功能,又或者需要做個截屏分享功能。最近正好在研究這些東西,寫篇博客整理一下。

view長按保存圖片的幾種方式

如果是網絡圖片,我們可以直接選擇將圖片下載下來後保存,這種方式,簡單暴力,可以直接獲得原圖,本質其實就是下載文件。代碼如下:

    public static boolean  downloadBitmap(String urlString, File fileDir) {
        URL url = null;
        InputStream is = null;
        FileOutputStream fos = null;
        HttpURLConnection conn=null;
        boolean isSuccess=false;
        try {

            url = new URL(urlString);
            //開啓連接
           conn = (HttpURLConnection) url.openConnection();
            //設置超時的時間,5000毫秒即5秒
            conn.setConnectTimeout(5000);
            //設置獲取圖片的方式爲GET
            conn.setRequestMethod("GET");
            conn.connect();
            //響應碼爲200,則訪問成功
            if (conn.getResponseCode() == 200) {
                //獲取連接的輸入流,這個輸入流就是圖片的輸入流
                is = conn.getInputStream();
                if (!fileDir.exists()) {
                    fileDir.mkdirs();
                }
                String fileName = urlString.substring(urlString.lastIndexOf("/") + 1); //根據鏈接獲取文件名
                File file = new File(fileDir, fileName);
                fos = new FileOutputStream(file);
                int len;
                byte[] buffer = new byte[1024];
                //將輸入流寫入到我們定義好的文件中
                while ((len = is.read(buffer)) != -1) {
                    fos.write(buffer, 0, len);
                }
                //將緩衝刷入文件
                fos.flush();
                isSuccess=true;

            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(fos!=null){
                    fos.close();
                }
                if(is!=null){
                    is.close();
                }

                if(conn!=null){
                    conn.disconnect();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return isSuccess;

    }

上面的方法用於網絡圖片,好處是可以直接獲得原圖,缺點也顯而易見,必須依賴於網絡,並且可能會耗費用戶的流量。那麼問題來了,有沒有其他辦法可以直接保存ImageView裏面的圖片呢?幸運的是,的確可以做到,而且不止一種方式,下面我將逐一介紹。在介紹前,我們先來認識一下view裏面的幾個API:

  • setDrawingCacheEnabled

    Enables or disables the drawing cache. When the drawing cache is enabled, the next call to {@link #getDrawingCache()} or {@link #buildDrawingCache()} will draw the view in a bitmap. Calling {@link #draw(android.graphics.Canvas)} will not draw from the cache when the cache is enabled. To benefit from the cache, you must request the drawing cache by calling {@link #getDrawingCache()} and draw it on screen if the returned bitmap is not null.

    由API可知,setDrawingCacheEnabled爲true後,使用getDrawingCache會將該view繪製成bitmap並返回,setDrawingCacheEnabled爲false後,會清空緩存。

  • getDrawingCache

    Returns the bitmap in which this view drawing is cached. The returned bitmap is null when caching is disabled. If caching is enabled and the cache is not ready, this method will create it. Calling {@link #draw(android.graphics.Canvas)} will not draw from the cache when the cache is enabled. To benefit from the cache, you must request the drawing cache by calling this method and draw it on screen if the returned bitmap is not null.

    由API可知,只要開啓繪畫緩存之後,如果緩存還沒有準備好,就先創建緩存(將view繪製到bitmap中保存起來),創建緩存使用buildDrawingCache,可以手動調用,或者直接 getDrawingCache 則系統會自動創建緩存,並返回。

  • buildDrawingCache

    如果緩存無效,則開始構建緩存(將view繪製成bitmap)

  • destroyDrawingCache

    清空緩存,釋放緩存的資源佔用

在瞭解上面API,思路就已經很清晰了。以下是我整理的保存圖片的幾種方式。

  1. 開啓緩存保存圖片,適用於所有view。但是這種方式保存的圖片有個致命的缺點,所見即所得,比如ImageView的縮放類型爲ScaleType.CENTER_CROP,此時保存的也是縮放過後的圖片,這種效果可能有時無法滿足我們的實際需求。但也不是一無是處,在後面的截屏介紹中,這種方法非常強大。

    public static void saveBitmap(View view, String filePath){
        view.setDrawingCacheEnabled(true);//開啓繪製緩存
        Bitmap imageBitmap = view.getDrawingCache();
        FileOutputStream outStream = null;
        File file=new File(filePath);
        if(file.isDirectory()){//如果是目錄不允許保存
            return;
        }
        try {
            outStream = new FileOutputStream(file);
            imageBitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
            outStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
            if(outStream!=null){
                outStream.close();
            }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
            imageBitmap.recycle();
            view.setDrawingCacheEnabled(false);//關閉繪製緩存
        }
    
    }
  2. 上面的方式無法保存一張完整的圖片,所以可能無法滿足實際工作需求,這時就需要用到下面這種方法,這種方法可以獲取整個圖片,但是隻適用於ImageView。其他view只能使用getBackground來獲得Drawable對象,不滿足實際需求,而且Background也不一定是BitmapDrawable

    public static void saveBitmap(ImageView view, String filePath) {
        Drawable drawable = view.getDrawable();
        if (drawable == null) {
            return;
        }
        FileOutputStream outStream = null;
        File file = new File(filePath);
        if (file.isDirectory()) {//如果是目錄不允許保存
            return;
        }
        try {
            outStream = new FileOutputStream(file);
            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
            outStream.flush();
            bitmap.recycle();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (outStream != null) {
                    outStream.close();
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
  3. 這種方式適用於所有的view,可以將view裏的全部內容繪製成bitmap並保存。

    public static void saveBitmap(View view, String filePath){
    
        // 創建對應大小的bitmap
        Bitmap  bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
                Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        view.draw(canvas);
    
        //存儲
        FileOutputStream outStream = null;
        File file=new File(filePath);
        if(file.isDirectory()){//如果是目錄不允許保存
            return;
        }
        try {
            outStream = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
            outStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bitmap.recycle();
                if(outStream!=null){
                    outStream.close();
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    }

    關於長按保存圖片功能已經介紹完了,下面來看看常見的截屏保存圖片的方式。

常見的截屏保存圖片分享方式

  • 獲取當前屏幕截圖,包含狀態欄
    public static Bitmap snapShotWithStatusBar(Activity activity) {
        View view = activity.getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap bitmap= view.getDrawingCache();
        int width = getScreenWidth(activity);//獲取屏幕的寬
        int height = getScreenHeight(activity);//獲取屏幕的高
        Bitmap bm = null;
        bm = Bitmap.createBitmap(bitmap, 0, 0, width, height);
        view.destroyDrawingCache();
        return bm;

    }
  • 獲取當前屏幕截圖,不包含狀態欄
    public static Bitmap snapShotWithoutStatusBar(Activity activity) {
        View view = activity.getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap bitmap= view.getDrawingCache();
        Rect frame = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
        int statusBarHeight = frame.top;//獲取狀態欄的高
        int width = getScreenWidth(activity);//獲取屏幕的寬
        int height = getScreenHeight(activity);//獲取屏幕的高
        Bitmap bm = Bitmap.createBitmap(bitmap, 0, statusBarHeight, width, height - statusBarHeight);
        view.destroyDrawingCache();
        return bm;

    }
  • 獲取當前屏幕截圖,不包含狀態欄以及標題欄(ActionBar)
    public static Bitmap snapShotWithoutStatusBarAndTitle(Activity activity) {
        View view = activity.getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap bitmap= view.getDrawingCache();
        Rect frame = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
        int statusBarHeight = frame.top;//獲取狀態欄的高
        int titleBarHeight = activity.getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();//獲取標題欄的高
        int width = getScreenWidth(activity);//獲取屏幕的寬
        int height = getScreenHeight(activity);//獲取屏幕的高
        int totalBarHeight=titleBarHeight+statusBarHeight;
        Bitmap bm= Bitmap.createBitmap(bitmap, 0, totalBarHeight , width, height - totalBarHeight);
        view.destroyDrawingCache();
        return bm;
    }
  • 截取Scrollview等ViewGroup裏面的內容,常用於生成長微博,效果可參照簡書生成圖片分享
    public static Bitmap snapShotForViewGroup(ViewGroup view) {
        int totalHeight = 0;
        // 獲取ViewGroup實際高度
        for (int i = 0; i < view.getChildCount(); i++) {
            totalHeight += view.getChildAt(i).getHeight();
            //view.getChildAt(i).setBackgroundColor(0xffffffff);//這裏可以設置背景顏色,以免背景透明看不出效果
        }
        Bitmap bitmap= Bitmap.createBitmap(view.getWidth(), totalHeight,
                Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        view.draw(canvas);
        return bitmap;
    }
  • 截取WebView裏面的全面內容
    /**
     * Android 5.0以上 在Activity的setContentView之前要進行如下處理
     * if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
     *    WebView.enableSlowWholeDocumentDraw();
     * }
     * @param view
     * @return
     */
    public static Bitmap snapShotForWebView(WebView view) {
        float scale = view.getScale(); //獲取webview縮放率
        int webViewHeight = (int) (view.getContentHeight() * scale);//得到縮放後webview內容的高度
        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),webViewHeight,Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        view.draw(canvas);
        return bitmap;
    }
  • 自由截屏,即手指滑動區域截屏

    1. 首先針對整個DecorView開啓繪製緩存,然後根據傳入的Rect進行區域截屏。

      public static Bitmap snapShotByFinger(Activity activity,Rect rect) {
          View view = activity.getWindow().getDecorView();
          view.setDrawingCacheEnabled(true);
          view.buildDrawingCache();
          Bitmap bitmap = view.getDrawingCache();
          Bitmap bm = Bitmap.createBitmap(bitmap, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top);
          bitmap.recycle();
          view.destroyDrawingCache();
          return bm;
      
      }
    2. Rect需要通過TouchEvent來獲得

      private int left,top;
      @Override
      public boolean dispatchTouchEvent(MotionEvent event) {
          switch (event.getAction()){
              case MotionEvent.ACTION_DOWN://手指按下時,記錄當前座標
                  left = (int) event.getRawX();
                  top = (int) event.getRawY();
                  break;
              case MotionEvent.ACTION_UP://手指擡起時,記錄當前座標,然後進行換算,計算Rect;
                 int right = (int) event.getRawX();
                 int bottom = (int) event.getRawY();
                  Rect rect=new Rect();
                  if(right>left){
                      if(bottom>top){
                          rect.set(left,top,right,bottom);
                      }else{
                          rect.set(left,bottom,right,top);
                      }
                  }else{
                      if(bottom>top){
                          rect.set(right,top,left,bottom);
                      }else{
                          rect.set(right,bottom,left,top);
                      }
                  }
      
                  //截屏,保存圖片
                  String path = Environment.getExternalStorageDirectory().toString()+"/12345.png";
                  Util.saveBitmap(Util.snapShotByFinger(WebViewActivity.this,rect),path);
                  break;
          }
      
          return super.dispatchTouchEvent(event);
      }

暫時就整理這麼多東西,關於手指滑動截屏,實際應用中可能還需要處理滑動衝突,或者手指滑動過程中顯示路徑,這些東西大家自行研究吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章