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); } }