跟 miui 一样的自动滚动截屏

简介:跟 miui 一样的自动滚动截屏

很久之前写过一篇完全不同于其他长截屏方案的的博客,不过很仓促,现在重新整理一下 android 长截屏 beta1

miui 自动滚动长截屏效果

miui

  • 给滚动控件外面嵌套一个FrameLayoutLinearLayout等也可以)
  • 手动调用FrameLayoutdraw方法把view画到bitmap

    Bitmap bitmap = Bitmap.createBitmap(container.getWidth(), container.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    container.draw(canvas);
    

    具体参考 DrawScrollViewAct DrawListViewAct

  • 通过不断改变 motionEvent 的 y 值并手动调用 view 的dispatchTouchEvent方法实现view滚动

 private void autoScroll() {
        final int delay = 16;
        final MotionEvent motionEvent = MotionEvent.obtain(SystemClock.uptimeMillis()
                , SystemClock.uptimeMillis()
                , MotionEvent.ACTION_DOWN
                , listView.getWidth() / 2
                , listView.getHeight() / 2
                , 0);
        listView.dispatchTouchEvent(motionEvent);//先分发 MotionEvent.ACTION_DOWN 事件

        listView.postDelayed(new Runnable() {
            @Override
            public void run() {
                motionEvent.setAction(MotionEvent.ACTION_MOVE); //延时分发 MotionEvent.ACTION_MOVE 事件
                //改变 y 坐标,越大滚动越快,但太大可能会导致掉帧
                motionEvent.setLocation((int) motionEvent.getX(), (int) motionEvent.getY() - 10);
                listView.dispatchTouchEvent(motionEvent);
                listView.postDelayed(this, delay);
            }
        }, delay);
    }

参考 ScrollAct

自动滚动效果 自动滚动

截屏
  • 每自动滚动完一屏幕调用view.draw()view画到bitmap上,最后拼接 bitmap

    参考 AutoScreenShotsAct

为什么要嵌套一层 view

listview 不嵌套时,不管是否滚动,都能得到正确的结果

listview

但 scrollview 滚动后即使顶部的已经看不到了,但调用 scrollview 的 draw 时还是会把 scrollview 不可见的地方画进去 scrollview

为了通用起间,我们给 view 外面嵌套了一层 view

代码实现

关键逻辑如下,但有些细节还需要具体对待


  private void autoScroll() {
        final int delay = 16;//延时 16 毫秒分发滑动事件
        final int step = 10;//每次滑动距离 5 像素,可以根据需要调整(若卡顿的话实际滚动距离可能小于 5)
        final MotionEvent motionEvent = MotionEvent.obtain(SystemClock.uptimeMillis()
                , SystemClock.uptimeMillis()
                , MotionEvent.ACTION_DOWN
                , listView.getWidth() / 2
                , listView.getHeight() / 2
                , 0);
        //先分发 MotionEvent.ACTION_DOWN 事件,我们指定为按下位置是 listview 的中间位置,当然其他位置也可以
        listView.dispatchTouchEvent(motionEvent);
        /*
        注意:
        查看 Listview 源码可知 滑动距离大于 ViewConfiguration.get(view.getContext()).getScaledTouchSlop()时 listview 才开始滚动
         private boolean startScrollIfNeeded(int x, int y, MotionEvent vtev) {
            // Check if we have moved far enough that it looks more like a
            // scroll than a tap
            final int deltaY = y - mMotionY;
            final int distance = Math.abs(deltaY);
            final boolean overscroll = mScrollY != 0;
            if ((overscroll || distance > mTouchSlop) && (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {
                ...
                return true;
            }
            return false;
        }
         */
        motionEvent.setAction(MotionEvent.ACTION_MOVE);
        motionEvent.setLocation(motionEvent.getX(), motionEvent.getY() - (ViewConfiguration.get(listView.getContext()).getScaledTouchSlop()));
        listView.dispatchTouchEvent(motionEvent);

        final int startScrollY = (int) motionEvent.getY();

        listView.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (isScreenShots == false) {//注意:我们无法通过滚动距离来判断是否滚动到了最后,所以需要通过其他方式停止滚动
                    drawRemainAndAssemble(startScrollY, (int) motionEvent.getY());
                    return;
                }
                //滚动刚好一整屏时画到 bitmap 上
                drawIfNeeded(startScrollY, (int) motionEvent.getY());

                motionEvent.setAction(MotionEvent.ACTION_MOVE); //延时分发 MotionEvent.ACTION_MOVE 事件
                /*
                  改变 motionEvent 的 y 坐标,nextStep 越大滚动越快,但太大可能会导致掉帧,导致实际滚动距离小于我们滑动的距离

                  因为我们是根据(curScrollY - startScrollY) % container.getHeight() == 0 来判定是否刚好滚动了一屏幕的,
                  所以快要滚动到刚好一屏幕位置时,修改 nextStep 的值,使下次滚动后刚好是一屏幕的距离。
                  当然 nextStep 也可以一直是 1,这时就不需要凑整了,但这样会导致滚动的特别慢
                 */
                int nextStep;
                int gap = (startScrollY - (int) motionEvent.getY() + step) % container.getHeight();
                if (gap > 0 && gap < step) {
                    nextStep = step - gap;
                } else {
                    nextStep = step;
                }

                motionEvent.setLocation((int) motionEvent.getX(), (int) motionEvent.getY() - nextStep);
                listView.dispatchTouchEvent(motionEvent);

                listView.postDelayed(this, delay);
            }
        }, delay);
    }

    private void drawRemainAndAssemble(int startScrollY, int curScrollY) {
        //最后的可能不足一屏幕,需要单独处理
        if ((curScrollY - startScrollY) % container.getHeight() != 0) {
            Bitmap film = Bitmap.createBitmap(container.getWidth(), container.getHeight(), Bitmap.Config.RGB_565);
            Canvas canvas = new Canvas();
            canvas.setBitmap(film);
            container.draw(canvas);

            int part = (startScrollY - curScrollY) / container.getHeight();
            int remainHeight = startScrollY - curScrollY - container.getHeight() * part;
            Bitmap remainBmp = Bitmap.createBitmap(film, 0, container.getHeight() - remainHeight, container.getWidth(), remainHeight);
            bitmaps.add(remainBmp);
        }

        assembleBmp();

    }

    private void assembleBmp() {
        int h = 0;
        for (Bitmap bitmap : bitmaps) {
            h += bitmap.getHeight();
        }
        //如果你需要透明度或者对图片质量要求很高的话请使用 Config.ARGB_8888
        Bitmap bitmap = Bitmap.createBitmap(container.getWidth(), h, Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        for (Bitmap b : bitmaps) {
            canvas.drawBitmap(b, 0, 0, null);
            canvas.translate(0, b.getHeight());
        }
        ViewGroup.LayoutParams params = img.getLayoutParams();
        params.width = bitmap.getWidth() * 2;
        params.height = bitmap.getHeight() * 2;
        img.requestLayout();
        img.setImageBitmap(bitmap);
    }

    private void drawIfNeeded(int startScrollY, int curScrollY) {

        if ((curScrollY - startScrollY) % container.getHeight() == 0) {
            //正好滚动满一屏

            //为了更通用,我们是把 ListView 的父布局(和 ListView 宽高相同)画到了 bitmap 上
            Bitmap film = Bitmap.createBitmap(container.getWidth(), container.getHeight(), Bitmap.Config.RGB_565);
            Canvas canvas = new Canvas();
            canvas.setBitmap(film);
            container.draw(canvas);
            bitmaps.add(film);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值