安卓手机如何截长屏,安卓最新手机截长屏的软件

首页 > 经验 > 作者:YD1662022-10-26 22:50:20

使用低版本 miui(这里 miui8)手机,获取对应的代码:/system/framework/framework.jar 或 github 查找 miui 开放代码。

实现原理介绍

整体流程:查找滚动视图 → 驱动视图滚动 → 分段截图→截图内容合并

查找滚动视图

安卓手机如何截长屏,安卓最新手机截长屏的软件(5)

其中检查条件:

  1. 1. View visibility == View.VISIBLE

  2. 2. canScrollVertically(1) == true

  3. 3. View 在屏幕内的宽度 > 屏幕宽度/3

  4. 4. View 在屏幕内的高度 > 屏幕高度/2

触发视图滚动

安卓手机如何截长屏,安卓最新手机截长屏的软件(6)

  1. 1. 每次滚动前,使用 canScrollVertically(1) 判断是否向下滚动

  2. 2. 触发滚动逻辑

    1. a. 特殊视图: dispatchFakeTouchEvent(2);private boolean checkNeedFakeTouchForScroll {
      if ((this.mMainScrollView instanceof AbsListView) ||
      (this.mMainScrollView instanceof ScrollView) ||
      isRecyclerView(this.mMainScrollView.getClass) ||
      isNestedScrollView(this.mMainScrollView.getClass)) {
      return false;
      }
      return !(this.mMainScrollView instanceof AbsoluteLayout) ||
      (Build.VERSION.SDK_INT > 19 &&
      !"com.ucmobile".equalsIgnoreCase(this.mMainScrollView.getContext.getPackageName) &&
      !"com.eg.android.AlipayGphone".equalsIgnoreCase(this.mMainScrollView.getContext.getPackageName));
      }

    2. b. AbsListView: scrollListBy(distance);

    3. c. 其他:view.scrollBy(0, distance);

  3. 3. 滚动结束,对比 scrollY 和 mPrevScrolledY 是否相同,相同则认为触底,停止滚动流程

生成长截图

每次滚动后广播,触发 mMainScrollView 局部截图,最后生成多个 Bitmap,最后合成 File 文件。在适配 Flutter 页面,这里并没有差异,所以这里就不做源码解读(不同 Miui 版本实现也有所不同)。

闲鱼适配方案

Flutter 长截屏不适配原因

通过分析源码可知,Flutter 容器(SurfaceView/TextureView) canScrollVertically 方法并未被重写,为此无法被找到作为 mMainScrollView。假如我们重写 Flutter 容器,我们需要真实实现 getScrollY 才能保证触发滚动后 scrollY 和 mPrevScrolledY 不相等。不幸的是,getScrollY 是 final 类型,无法被继承类重写,为此我们无法在 Flutter 容器上做处理。

@InspectableProperty
public final int getScrollY {
return mScrollY;
}

系统事件代理

转变思路,我们并不需要让 Flutter 容器被 Miui 系统识为可滚动视图,而是让 Flutter 接收到 Miui 系统指令。为此,我们构建一个不可见、不影响交互的滚动视图 ControlView 被 Miui 系统识别,并接收系统指令。ControlView 最后把指令传递给 Flutter,最终建立了 Miui 系统(ContentPort)和闲鱼 Flutter(可滚动 RenderObject)之间的通信。

其中通信事件:

  1. 1. void scrollBy(View view, int x, int y)

  2. 2. boolean canScrollVertically(View view, int direction, boolean startScreenshot)

  3. 3. int getScrollY(View view)

安卓手机如何截长屏,安卓最新手机截长屏的软件(7)

关键实现源码如下

public static FrameLayout setupLongScreenshotSupport(FrameLayout parent,
View targetChild,
IMiuiLongScreenshotViewDelegate delegate) {

Context context = targetChild.getContext;

MiuiLongScreenshotView screenshotView = new MiuiLongScreenshotView(context);
screenshotView.setDelegate(delegate);
screenshotView.addView(targetChild, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));

MiuiLongScreenshotControlView controlView = new MiuiLongScreenshotControlView(context);
controlView.bindRealScrollView(screenshotView);

if (parent == ) {
parent = new FrameLayout(context);
}
parent.addView(screenshotView, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
parent.addView(controlView);
return parent;
}

public class MiuiLongScreenshotControlView extends ScrollView
implements MiuiScreenshotBroadcast.IListener {

private IMiuiLongScreenshotView mRealView;
...

public void bindRealScrollView(IMiuiLongScreenshotView v) {
mRealView = v;
removeAllViews;

Context context = getContext;
LinearLayout ll = new LinearLayout(context);
addView(ll);

View btn = new View(context);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
UIUtil.dp2px(context, 20000));
ll.addView(btn, lp);

resetScrollY(true);
}

public void resetScrollY(boolean startScreenshot) {
if (mRealView != ) {
setScrollY(0);
if (getWindowVisibility == VISIBLE) {
ThreadUtil.runOnUI(
-> mRealView.canScrollVertically(1, startScreenshot));
}
}
}

@Override
public void onReceiveScreenshot {
// 每次收到截屏广播,将 ControlView 滚动距离置 0
// 提前查找滚动 RenderObject 并缓存
// 提前计算 canScrollVertically
resetScrollY(true);
}

@Override
protected void onAttachedToWindow {
super.onAttachedToWindow;

mContext = getContext;
// 截屏广播监听
MiuiScreenshotBroadcast.register(mContext, this);
}

@Override
protected void onDetachedFromWindow {
super.onDetachedFromWindow;
MiuiScreenshotBroadcast.unregister(mContext, this);
}

@Override
public boolean canScrollVertically(int direction) {
if (mRealView != ) {
return mRealView.canScrollVertically(direction, false);
}
return super.canScrollVertically(direction);
}

@Override
public void scrollBy(int x, int y) {
super.scrollBy(x, y);
if (mRealView != ) {
mRealView.scrollBy(x, y);
}
}

// 代理获取 DrawingCache
@Override
public void setDrawingCacheEnabled(boolean enabled) {
super.setDrawingCacheEnabled(enabled);
if (mRealView != ) {
mRealView.setDrawingCacheEnabled(enabled);
}
}

@Override
public boolean isDrawingCacheEnabled {
if (mRealView != ) {
return mRealView.isDrawingCacheEnabled;
}
return super.isDrawingCacheEnabled;
}

@Override
public Bitmap getDrawingCache(boolean autoScale) {
Bitmap result = (mRealView != )
? mRealView.getDrawingCache(autoScale)
: super.getDrawingCache(autoScale);
return result;
}

@Override
public void destroyDrawingCache {
super.destroyDrawingCache;
if (mRealView != ) {
mRealView.destroyDrawingCache;
}
}

@Override
public void buildDrawingCache(boolean autoScale) {
super.buildDrawingCache(autoScale);
if (mRealView != ) {
mRealView.buildDrawingCache(autoScale);
}
}

// 不消费屏幕操作事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
return false;
}
}

无侵入识别滚动区域

获取 RenderObject 根节点

使用 mixin 扩展 WidgetsFlutterBinding,进而获取 RenderView

关键实现源码如下:

mixin NativeLongScreenshotFlutterBinding on WidgetsFlutterBinding {

@override
void initInstances {
super.initInstances;
// 初始化
FlutterMiuiLongScreenshotPlugin.inst;
}

@override
void handleDrawFrame {
super.handleDrawFrame;
try {
NativeLongScreenshot.singleInstance._renderView = renderView;
} catch (error, stack) {
}
}
}

计算前台滚动 RenderObject

安卓手机如何截长屏,安卓最新手机截长屏的软件(8)

上一页12345下一页

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.