小米系统自带的长截屏应该很多人都用过,效果不错。当长截屏时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<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个像素,当发现下次再滚动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
# 长截屏
# 实现
# 长截屏原理
# Android截屏方案实现原理解析
# Android截屏分享功能
# Android中关于屏幕的三个小众知识(宽屏适配、禁止截屏和保持屏幕常亮)
# Android 下调试手机截屏的方法
# Android 实现截屏功能的实例
# Android实现截屏方式整理(总结)
# android视频截屏&手机录屏实现代码
# 浅谈Android截屏和指定View生成截图
# 我们可以
# 当我们
# 就会
# 过大
# 创建一个
# 这层
# 都在
# 都要
# 也要
# 要不要
# 很多人
# 如果没有
# 可以看到
# 就不能
# 只需要
# 用过
# 时就
# 按下
# 这块
# 在一
相关文章:
完全自定义免费建站平台:主题模板在线生成一站式服务
广东专业制作网站有哪些,广东省能源集团有限公司官网?
香港服务器租用费用高吗?如何避免常见误区?
建站10G流量真的够用吗?如何应对访问高峰?
Swift中循环语句中的转移语句 break 和 continue
如何在香港服务器上快速搭建免备案网站?
javascript中对象的定义、使用以及对象和原型链操作小结
电视网站制作tvbox接口,云海电视怎样自定义添加电视源?
MySQL查询结果复制到新表的方法(更新、插入)
公司门户网站制作流程,华为官网怎么做?
制作网站建设的公司有哪些,网站建设比较好的公司都有哪些?
如何制作公司的网站链接,公司想做一个网站,一般需要花多少钱?
如何在万网开始建站?分步指南解析
网站建设制作需要多少钱费用,自己做一个网站要多少钱,模板一般多少钱?
定制建站流程步骤详解:一站式方案设计与开发指南
标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?
如何基于云服务器快速搭建个人网站?
如何在云虚拟主机上快速搭建个人网站?
教学论文网站制作软件有哪些,写论文用什么软件
?
青浦网站制作公司有哪些,苹果官网发货地是哪里?
建站之星CMS五站合一模板配置与SEO优化指南
广州建站公司哪家好?十大优质服务商推荐
如何通过.red域名打造高辨识度品牌网站?
SQL查询语句优化的实用方法总结
C++中的Pimpl idiom是什么,有什么好处?(隐藏实现)
制作网站哪家好,cc、.co、.cm哪个域名更适合做网站?
如何在宝塔面板中修改默认建站目录?
网站企业制作流程,用什么语言做企业网站比较好?
网页设计与网站制作内容,怎样注册网站?
制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?
已有域名和空间如何搭建网站?
如何在搬瓦工VPS快速搭建网站?
零服务器AI建站解决方案:快速部署与云端平台低成本实践
如何访问已购建站主机并解决登录问题?
猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?
非常酷的网站设计制作软件,酷培ai教育官方网站?
家庭服务器如何搭建个人网站?
如何选择适合PHP云建站的开源框架?
网站专业制作公司有哪些,做一个公司网站要多少钱?
建站之星后台管理系统如何操作?
如何快速登录WAP自助建站平台?
如何获取上海专业网站定制建站电话?
h5网站制作工具有哪些,h5页面制作工具有哪些?
c++怎么使用类型萃取type_traits_c++ 模板元编程类型判断【方法】
如何生成腾讯云建站专用兑换码?
php8.4新语法match怎么用_php8.4match表达式替代switch【方法】
seo网站制作优化,网站SEO优化步骤有哪些?
专业商城网站制作公司有哪些,pi商城官网是哪个?
公司网站制作价格怎么算,公司办个官网需要多少钱?
建站之星多图banner生成与模板自定义指南
*请认真填写需求信息,我们会在24小时内与您取得联系。