Auto scroll screen capture like miui

Keywords: less Android MIUI

Project address: android-notes/auto-scroll-capture
Introduction: automatic scrolling screenshots like miui

I wrote a blog a long time ago, which is totally different from other long screenshots, but it's very hasty. Now I'll rearrange it android long screenshot beta1

miui auto scroll long screenshot effect

painting

  • Nest a FrameLayout (LinearLayout, etc.) outside the scroll control
  • Manually call the draw method of FrameLayout to draw the view onto the bitmap

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

    For details, refer to DrawScrollViewAct DrawListViewAct

rolling

  • View scrolling is realized by constantly changing y value of motionEvent and manually calling dispatchTouchEvent method of 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);//Distribute motionevent.action'down event first

        listView.postDelayed(new Runnable() {
            @Override
            public void run() {
                motionEvent.setAction(MotionEvent.ACTION_MOVE); //Delay the distribution of motionevent.action'move event
                //Change the y coordinate, the larger the scrolling speed is, but too much may cause frame dropping
                motionEvent.setLocation((int) motionEvent.getX(), (int) motionEvent.getY() - 10);
                listView.dispatchTouchEvent(motionEvent);
                listView.postDelayed(this, delay);
            }
        }, delay);
    }

Refer to ScrollAct

Auto scroll effect

Screenshots

  • Call view.draw() to draw the view onto the bitmap after each automatic screen scrolling, and finally splice it bitmap

    Refer to autoscreen shotact

Why to nest a view

When listview is not nested, whether it scrolls or not, the correct result can be obtained

However, even if the top part of scrollview is no longer visible after scrolling, when calling the draw of scrollview, the invisible part of scrollview will still be drawn in

For general purpose, we nest a layer of view outside the view

code implementation

The key logic is as follows, but some details need to be treated specifically

  private void autoScroll() {
        final int delay = 16;//16 ms delay to distribute sliding events
        final int step = 10;//Each sliding distance is 5 pixels, which can be adjusted as needed (the actual rolling distance may be less than 5 if it is stuck)
        final MotionEvent motionEvent = MotionEvent.obtain(SystemClock.uptimeMillis()
                , SystemClock.uptimeMillis()
                , MotionEvent.ACTION_DOWN
                , listView.getWidth() / 2
                , listView.getHeight() / 2
                , 0);
        //First, distribute the motionevent.action'down event. We specify that the pressing position is the middle position of listview. Of course, other positions can also be used
        listView.dispatchTouchEvent(motionEvent);
        /*
        be careful:
        It can be seen from the listview source code that listview starts to scroll only when the sliding distance is greater than ViewConfiguration.get(view.getContext()).getScaledTouchSlop()
         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) {//Note: we can't judge whether we have scrolled to the end by scrolling distance, so we need to stop scrolling in other ways
                    drawRemainAndAssemble(startScrollY, (int) motionEvent.getY());
                    return;
                }
                //Scroll just one full screen and draw on bitmap
                drawIfNeeded(startScrollY, (int) motionEvent.getY());

                motionEvent.setAction(MotionEvent.ACTION_MOVE); //Delay the distribution of motionevent.action'move event
                /*
                  By changing the y coordinate of motionEvent, the larger nextStep is, the faster the scrolling is. However, if it is too large, it may cause frame dropping, resulting in the actual scrolling distance less than our sliding distance

                  Because we judge whether we have just scrolled one screen according to (curscroll - startscroll)% container. Getheight() = = 0,
                  So when you are about to scroll to a screen position, change the value of nextStep so that the next time you scroll, it will be a screen distance.
                  Of course, nextStep can always be 1, so there is no need to round it up, but this will cause the scrolling to be particularly slow
                 */
                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) {
        //The last one may be less than one screen, which needs to be handled separately
        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();
        }
        //If you need transparency or high quality pictures, please use config.argb
        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) {
            //Just one full screen

            //In order to be more general, we drew the parent layout of ListView (the same width and height as ListView) onto the 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);
        }
    }

Posted by Nukeum66 on Sat, 02 May 2020 11:18:26 -0700