android长截屏原理及实现代码

发布时间:2020-08-31 06:55:16

来源:脚本之家

阅读:158

作者:Android笔记

小米系统自带的长截屏应该很多人都用过,效果不错。当长截屏时listview就会自动滚动,当按下停止截屏时,就会得到一张完整的截屏。

该篇就介绍一下长截屏的原理

上篇中介绍了android屏幕共享实现方式,该篇的原理和上一篇基本一致。

获取view影像

当我们想得到一个view的影像时,我们可以调用系统api,得到view的bitmap,但有时可能得不到。我们可以通过另一种方式得到。

首先创建一个和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);

执行完上面代码后bmp上就是view的影像了。

制造滚动事件,促使view滚动

我们可以创建一个MotionEvent,然后定时修改MotionEvent的y值,并分发给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);

//把事件分发给view

view.dispatchTouchEvent(motionEvent);

view.postDelayed(this, DELAY);

}

}, DELAY);

注意:从分发DOWN事件到结束都要使用同一个MotionEvent对象,只需要不断改变x或y值。

每次x或y的值相对于上次改动不能过大,若过大,view实际滚动距离可能达不到为MotionEvent设置的值(因view滚动时卡顿导致)。

截屏

当为MotionEvent设置的x或y值正好时当前view的大小时,创建新的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的事件分发相关函数得到(dispatchTouchEvent),让Listview认为是开始滚动,这样才能保证以后分发的滑动距离和实际滚动距离一致。

Listview也要通知是否滚动到了最后,不然如果没有手动停止的话,虽然还是在一直分发滚动事件,但ListView不再滚动,导致最终截图后后面全是重复的最后一屏幕。

附 实现大致方式代码,有待优化

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 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个像素,当发现下次再滚动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);

}

布局

android:layout_width="match_parent"

android:layout_height="match_parent"

android:id="@+id/view"

android:orientation="vertical"

>

android:id="@+id/listview"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:divider="#e1e1e1"

android:dividerHeight="2dp"

>

效果图

屏幕

最终截屏

可以看到毫无拼接痕迹。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持亿速云。

android长截屏代码,android长截屏原理及实现代码相关推荐

  1. java加密解密代码_base64位加密解密原理及js代码实现

    base64位加密解密原理及js代码实现 在网上找了很多关于Base64加密解密的原理以一个比较通俗易懂的方式理解整理了一下大致原理如下 先上base64对照表 #加密 #1:将明文对照以acsii码 ...

  2. 随机森林分类算法python代码_随机森林的原理及Python代码实现

    原标题:随机森林的原理及Python代码实现 最近在做kaggle的时候,发现随机森林这个算法在分类问题上效果十分的好,大多数情况下效果远要比svm,log回归,knn等算法效果好.因此想琢磨琢磨这个 ...

  3. html打折代码,HTML打折计算价格原理与脚本代码

    大概原理就是设置计算价格事件函数,取不同下拉打折数,计算结果送入文字框,感兴趣的朋友可以参考下 代码如下: 打折后价格计算 function calculator(){ var prices=docu ...

  4. Android截屏与WebView长图分享经验总结【转】

    原文:https://youzanmobile.github.io/2017/05/19/android-screenshot-and-webview/ 最近在做新业务需求的同时,我们在 Androi ...

  5. Android 截屏与 WebView 长图分享经验总结

    最近在做新业务需求的同时,我们在 Android 上遇到了一些之前没有碰到过的问题,截屏分享. WebView 生成长图以及长图在各个分享渠道分享时图片模糊甚至分享失败等问题,在这过程中踩了很多坑,到 ...

  6. android写代码截屏微信,android 模仿微信头像裁剪

    android 在android开发中经常会碰到修改头像的功能需求, 而在修改头像>中最难的部分就是在头像的裁剪功能.虽说网上一大堆裁剪的例子,但最重要的是要理解里面的实现原理,这样以后自己改起 ...

  7. android录屏弹窗,android视频截屏手机录屏实现代码

    本文介绍了android视频截屏&手机录屏实现代码,分享给大家,希望对大家有帮助 问题 在android中有时候我们需要对屏幕进行截屏操作,单一的截屏操作好解决可以通过activity的顶层v ...

  8. 【Android 内存优化】自定义组件长图组件 ( 长图滚动区域解码 | 手势识别 GestureDetector | 滑动计算类 Scroller | 代码示例 )

    文章目录 一.GestureDetector 创建与设置 二.GestureDetector 触摸事件传递 三.触摸滑动操作 四.惯性滑动操作 五.长图滑动组件代码示例 六.运行效果 七.源码及资源下 ...

  9. android华为虚拟截屏黑屏,Android截屏表面视图显示黑屏

    Android截屏表面视图显示黑屏 我试图通过代码拍摄我的游戏的截图,并通过一个意图来分享它.我能做这些事情,但是截图总是看起来是黑色的.下面是与分享截图相关的代码:View view = MainA ...

最新文章

  1. 如何看待机器视觉的“对抗样本”问题,其原理是什么?
  2. JS、Flash 实现复制功能 (浏览器兼容)
  3. GBDT!深入浅出详解梯度提升决策树
  4. angular 内容投影
  5. .NET 4.0的犄角旮旯
  6. lda主题模型困惑度_主题模型(三):LDA主题个数选择
  7. html 中的一些知识
  8. opencv 鼠标点击处视频的坐标和rgbw值
  9. Win10 wifi、蓝牙打不开
  10. DataTable转换为ListT或者DataRow转换为T
  11. java游戏开局选宠物可以转职,创世之光人物资料及转职大全
  12. 在线传输文件的方法有哪些?镭速云帮您领略最快文件传输的攻略!
  13. Linux 快速修改某个分区的名称/标签
  14. 米勒-拉宾素性检测算法
  15. 国密算法—SM2介绍及基于BC的实现
  16. 汉语中的 熟语中的成语900个
  17. html5多媒体播放器,走进HTML5-学习多媒体,带你实现视频播放器、音乐播放器功能(*^▽^*)...
  18. 2.浅浅体验Python编程
  19. 一文带你读懂HTTP协议的前世今生
  20. 修改Centos7/RHEL7的主机名

热门文章

  1. 向Access中插入数据报“INSERT INTO 语句的语法错误” 解决
  2. cbow word2vec 损失_Word2Vec的核心思想
  3. md文件生成Java代码_Beetlsql自定义生成entity,mapper,md代码
  4. python读取excel指定行列_pandas读取excel指定行列索引header和index_col参数
  5. 每周分享第 19 期(20190426)
  6. centos7上配置Samba服务器完成与windows的文件共享
  7. 面向对象(继承,多态)
  8. 美国无人驾驶立法提案将进行投票,有望解除一切封杀
  9. webpack vue-cli 一有空格和分号就报错
  10. 《Access 2007开发指南(修订版)》一一1.11 额外的提示和技巧