溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

android長截屏原理及實現代碼

發(fā)布時間:2020-08-31 06:55:16 來源:腳本之家 閱讀:459 作者:Android筆記 欄目:移動開發(fā)

小米系統自帶的長截屏應該很多人都用過,效果不錯。當長截屏時listview就會自動滾動,當按下停止截屏時,就會得到一張完整的截屏。

該篇就介紹一下長截屏的原理

上篇中介紹了android屏幕共享實現方式,該篇的原理和上一篇基本一致。

獲取view影像

當我們想得到一個view的影像時,我們可以調用系統api,得到view的bitmap,但有時可能得不到。我們可以通過另一種方式得到。

首先創(chuàng)建一個和view一樣大小的bitmap

復制代碼 代碼如下:

Bitmap bmp = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.RGB_565);

然后把view繪制到bmp上

Canvas canvas = new Canvas(); 
 
canvas.setBitmap(bmp); 
 
view.draw(canvas);

執(zhí)行完上面代碼后bmp上就是view的影像了。

制造滾動事件,促使view滾動

我們可以創(chuàng)建一個MotionEvent,然后定時修改MotionEvent的y值,并分發(fā)給view,從而促使view上下滾動。當然我們也可以定時修改x值促使view左右滾動。

代碼大致如下

final MotionEvent motionEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, view.getWidth() / 2, view.getHeight() / 2, 0);  
    
view.postDelayed(new Runnable() { 
      @Override 
      public void run() { 
        
        motionEvent.setAction(MotionEvent.ACTION_MOVE); 
 
        motionEvent.setLocation((int) motionEvent.getX(), (int) motionEvent.getY() - 1); 
    //把事件分發(fā)給view
        view.dispatchTouchEvent(motionEvent); 
        
        view.postDelayed(this, DELAY); 
      } 
   }, DELAY);

注意:從分發(fā)DOWN事件到結束都要使用同一個MotionEvent對象,只需要不斷改變x或y值。

每次x或y的值相對于上次改動不能過大,若過大,view實際滾動距離可能達不到為MotionEvent設置的值(因view滾動時卡頓導致)。

截屏

當為MotionEvent設置的x或y值正好時當前view的大小時,創(chuàng)建新的bitmap,通過上述方法把view繪制到bitmap上,想要停止截屏時拼接所有bitmap即可。

備注

當我們想要把Listview長截屏時,需要為ListView外面嵌套一層和ListView一樣大小的View,以上的所有操作都在嵌套的這層view上操作。當我們調用嵌套的這層view的draw(new Canvas(bmp))時會把當前看到的這塊ListView繪制到bmp上,不管ListView嵌套了多少層子view都可以繪制到當前bmp上。

由于ListView中根據滑動的距離是否大于ViewConfiguration.get(view.getContext()).getScaledTouchSlop() )來確定要不要滾動,所以一開始我們要特殊處理下,為什么是ViewConfiguration.get(view.getContext()).getScaledTouchSlop() )可以查看ListView的事件分發(fā)相關函數得到(dispatchTouchEvent),讓Listview認為是開始滾動,這樣才能保證以后分發(fā)的滑動距離和實際滾動距離一致。

Listview也要通知是否滾動到了最后,不然如果沒有手動停止的話,雖然還是在一直分發(fā)滾動事件,但ListView不再滾動,導致最終截圖后后面全是重復的最后一屏幕。

附 實現大致方式代碼,有待優(yōu)化

package com.example.wanjian.test;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Environment;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
/**
 * Created by wanjian on 16/8/18.
 */
public class ScrollableViewRECUtil {
  public static final int VERTICAL = 0;
  private static final int DELAY = 2;
  private List<Bitmap> bitmaps = new ArrayList<>();
  private int orientation = VERTICAL;
  private View view;
  private boolean isEnd;
  private OnRecFinishedListener listener;
  public ScrollableViewRECUtil(View view, int orientation) {
    this.view = view;
    this.orientation = orientation;
  }
  public void start(final OnRecFinishedListener listener) {
    this.listener = listener;
    final MotionEvent motionEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, view.getWidth() / 2, view.getHeight() / 2, 0);
    view.dispatchTouchEvent(motionEvent);
    motionEvent.setAction(MotionEvent.ACTION_MOVE);
    //滑動距離大于ViewConfiguration.get(view.getContext()).getScaledTouchSlop()時listview才開始滾動
    motionEvent.setLocation(motionEvent.getX(), motionEvent.getY() - (ViewConfiguration.get(view.getContext()).getScaledTouchSlop() + 1));
    view.dispatchTouchEvent(motionEvent);
    motionEvent.setLocation(motionEvent.getX(), view.getHeight() / 2);
    view.postDelayed(new Runnable() {
      @Override
      public void run() {
        if (isEnd) {
          //停止時正好一屏則全部繪制,否則繪制部分
          if ((view.getHeight() / 2 - (int) motionEvent.getY()) % view.getHeight() == 0) {
            Bitmap bitmap = rec();
            bitmaps.add(bitmap);
          } else {
            Bitmap origBitmap = rec();
            int y = view.getHeight() / 2 - (int) motionEvent.getY();
            Bitmap bitmap = Bitmap.createBitmap(origBitmap, 0, view.getHeight() - y % view.getHeight(), view.getWidth(), y % view.getHeight());
            bitmaps.add(bitmap);
            origBitmap.recycle();
          }
          //最后一張可能高度不足view的高度
          int h = view.getHeight() * (bitmaps.size() - 1);
          Bitmap bitmap = bitmaps.get(bitmaps.size() - 1);
          h = h + bitmap.getHeight();
          Bitmap result = Bitmap.createBitmap(view.getWidth(), h, Bitmap.Config.RGB_565);
          Canvas canvas = new Canvas();
          canvas.setBitmap(result);
          for (int i = 0; i < bitmaps.size(); i++) {
            Bitmap b = bitmaps.get(i);
            canvas.drawBitmap(b, 0, i * view.getHeight(), null);
            b.recycle();
          }
          listener.onRecFinish(result);
          return;
        }
        if ((view.getHeight() / 2 - (int) motionEvent.getY()) % view.getHeight() == 0) {
          Bitmap bitmap = rec();
          bitmaps.add(bitmap);
        }
        motionEvent.setAction(MotionEvent.ACTION_MOVE);
  //模擬每次向上滑動一個像素,這樣可能導致滾動特別慢,實際使用時可以修改該值,但判斷是否正好滾動了
  //一屏幕就不能簡單的根據 (view.getHeight() / 2 - (int) motionEvent.getY()) % view.getHeight() == 0 來確定了。
  //可以每次滾動n個像素,當發(fā)現下次再滾動n像素時就超出一屏幕時可以改變n的值,保證下次滾動后正好是一屏幕,
  //這樣就可以根據(view.getHeight() / 2 - (int) motionEvent.getY()) % view.getHeight() == 0來判斷要不要截屏了。
        motionEvent.setLocation((int) motionEvent.getX(), (int) motionEvent.getY() - 1);
        view.dispatchTouchEvent(motionEvent);
        view.postDelayed(this, DELAY);
      }
    }, DELAY);
  }
  public void stop() {
    isEnd = true;
  }
  private Bitmap rec() {
    Bitmap film = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.RGB_565);
    Canvas canvas = new Canvas();
    canvas.setBitmap(film);
    view.draw(canvas);
    return film;
  }
  public interface OnRecFinishedListener {
    void onRecFinish(Bitmap bitmap);
  }
}

activity代碼

 setContentView(R.layout.activity_main4);
//
    listview= (ListView) findViewById(R.id.listview);
    listview.setAdapter(new BaseAdapter() {
      @Override
      public int getCount() {
        return 100;
      }
      @Override
      public Object getItem(int position) {
        return null;
      }
      @Override
      public long getItemId(int position) {
        return 0;
      }
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView==null){
          Button button= (Button) LayoutInflater.from(getApplication()).inflate(R.layout.item,listview,false);
          button.setText(""+position);
          return button;
        }
        ((Button)convertView).setText(""+position);
        return convertView;
      }
    });
//
    File file=new File(Environment.getExternalStorageDirectory(),"aaa");
    file.mkdirs();
    for (File f:file.listFiles()){
      f.delete();
    }
    listview.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
        listview.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        start();
      }
    });
private void start(){
    final View view=findViewById(R.id.view);
    final ScrollableViewRECUtil scrollableViewRECUtil=new ScrollableViewRECUtil(view,ScrollableViewRECUtil.VERTICAL);
    scrollableViewRECUtil.start(new ScrollableViewRECUtil.OnRecFinishedListener() {
      @Override
      public void onRecFinish(Bitmap bitmap) {
        File f= Environment.getExternalStorageDirectory();
        System.out.print(f.getAbsoluteFile().toString());
        Toast.makeText(getApplicationContext(),f.getAbsolutePath(),Toast.LENGTH_LONG).show();
        try {
          bitmap.compress(Bitmap.CompressFormat.JPEG,60,new FileOutputStream(new File(f,"rec"+System.currentTimeMillis()+".jpg")));
          Toast.makeText(getApplicationContext(),"Success",Toast.LENGTH_LONG).show();
        }catch (Exception e){
          e.printStackTrace();
        }
      }
    });
    // scrollableViewRECUtil
    view.postDelayed(new Runnable() {
      @Override
      public void run() {
        scrollableViewRECUtil.stop();
      }
    },90*1000);
  }

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:id="@+id/view"
  android:orientation="vertical"
 >
    <ListView
      android:id="@+id/listview"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:divider="#e1e1e1"
      android:dividerHeight="2dp"
      ></ListView>
</LinearLayout>

效果圖

屏幕

android長截屏原理及實現代碼 

最終截屏

android長截屏原理及實現代碼 

可以看到毫無拼接痕跡。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI