国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

android長截屏原理及實(shí)現(xiàn)代碼

2019-12-12 02:13:15
字體:
供稿:網(wǎng)友

小米系統(tǒng)自帶的長截屏應(yīng)該很多人都用過,效果不錯(cuò)。當(dāng)長截屏?xí)rlistview就會(huì)自動(dòng)滾動(dòng),當(dāng)按下停止截屏?xí)r,就會(huì)得到一張完整的截屏。

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

上篇中介紹了android屏幕共享實(shí)現(xiàn)方式,該篇的原理和上一篇基本一致。

獲取view影像

當(dāng)我們想得到一個(gè)view的影像時(shí),我們可以調(diào)用系統(tǒng)api,得到view的bitmap,但有時(shí)可能得不到。我們可以通過另一種方式得到。

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

復(fù)制代碼 代碼如下:

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的影像了。

制造滾動(dòng)事件,促使view滾動(dòng)

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

代碼大致如下

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事件到結(jié)束都要使用同一個(gè)MotionEvent對象,只需要不斷改變x或y值。

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

截屏

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

備注

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

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

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

附 實(shí)現(xiàn)大致方式代碼,有待優(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);    //滑動(dòng)距離大于ViewConfiguration.get(view.getContext()).getScaledTouchSlop()時(shí)listview才開始滾動(dòng)    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) {          //停止時(shí)正好一屏則全部繪制,否則繪制部分          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);  //模擬每次向上滑動(dòng)一個(gè)像素,這樣可能導(dǎo)致滾動(dòng)特別慢,實(shí)際使用時(shí)可以修改該值,但判斷是否正好滾動(dòng)了  //一屏幕就不能簡單的根據(jù) (view.getHeight() / 2 - (int) motionEvent.getY()) % view.getHeight() == 0 來確定了。  //可以每次滾動(dòng)n個(gè)像素,當(dāng)發(fā)現(xiàn)下次再滾動(dòng)n像素時(shí)就超出一屏幕時(shí)可以改變n的值,保證下次滾動(dòng)后正好是一屏幕,  //這樣就可以根據(jù)(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>

效果圖

屏幕

 

最終截屏

 

可以看到毫無拼接痕跡。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 金阳县| 松溪县| 凤山市| 九江县| 玉田县| 弥渡县| 隆昌县| 桦甸市| 军事| 灵石县| 图们市| 莫力| 建平县| 鄱阳县| 双城市| 海伦市| 徐闻县| 博客| 陵川县| 察雅县| 焉耆| 百色市| 玉林市| 东乌珠穆沁旗| 游戏| 上饶县| 城市| 江源县| 绥德县| 常德市| 三门峡市| 巨野县| 句容市| 清涧县| 德庆县| 桦川县| 南部县| 色达县| 襄垣县| 丽江市| 延吉市|