How to capture part of a ScrollView as its content

2019-08-14 14:40发布

问题:

After extending the ScrollView class I was able to easily be notified of the scrolling in realtime.

Now I need to capture the content of this scrollview in a very specific part. Let's say I want to capture the top of the screen (matching parent width and a defined height, like 100dp). But only the content of the ScrollView and not the rest, if there is anything else on the top but not as part of the ScrollView.

I tried using on the scrollview :

  1. setDrawingCacheEnabled(true);
  2. getDrawingCache(true);
  3. setDrawingCacheEnabled(false);

Then I tried to crop so that I get the part I want :

Bitmap.createBitmap(complete, 0, 0, width, height);

Results are very far from what I want to achieve and performance are very very poor and at some point I would get either a SIGENV or getDrawingCache(true) tries to use a recycled bitmap...

So how can I easily capture the content in the desired area without too much performance hit ?

Note: this process must be done as I am scrolling the content, so inside ScrollView's onScrollChanged(final int x, final int y).

Thanks !

回答1:

Since the problem was fun I implemented it, it seems to work fine. I guess that you are recreating a Bitmap each time that's why goes slow.

The idea is like this, you create an area in the ScrollView that you want to copy (see Rect cropRect and Bitmap screenshotBitmap), it's full width and you just need to set the height. The view automatically set a scroll listener on itself and on every scroll it will copy that area. Note that setDrawingCacheEanbled(true) is called just once when the view is instantiated, it basically tells the view that you will call getDrawingCache(), which will return the Bitmap on which the view is drawing itself. It then copy the area of interest on screenshotBitmap and that's the Bitmap that you might want to use.

ScreenshottableScrollView.java

package com.example.lelloman.screenshottablescrollview;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewTreeObserver;
import android.widget.ScrollView;

/**
 * Created by lelloman on 16-2-16.
 */
public class ScreenshottableScrollView extends ScrollView implements ViewTreeObserver.OnScrollChangedListener {

    public interface OnNewScreenshotListener {
        void onNewScreenshot(Bitmap bitmap);
    }

    private Bitmap screenshotBitmap = null;
    private Canvas screenshotCanvas = null;
    private int screenshotHeightPx = 0;
    private OnNewScreenshotListener listener = null;
    private Rect cropRect;
    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);


    public ScreenshottableScrollView(Context context) {
        super(context);
        init();
    }

    public ScreenshottableScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ScreenshottableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ScreenshottableScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init(){
        setDrawingCacheEnabled(true);
        getViewTreeObserver().addOnScrollChangedListener(this);
    }

    public void setOnNewScreenshotListener(OnNewScreenshotListener listener){
        this.listener = listener;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if(screenshotHeightPx != 0)
            makeScrenshotBitmap(w,h);
    }

    public void setScreenshotHeightPx(int q){
        screenshotHeightPx = q;
        makeScrenshotBitmap(getWidth(), getHeight());
    }

    private void makeScrenshotBitmap(int width, int height){

        if(screenshotBitmap != null) screenshotBitmap.recycle();

        if(width == 0 || height == 0) return;

        screenshotBitmap = Bitmap.createBitmap(width, screenshotHeightPx, Bitmap.Config.ARGB_8888);
        screenshotCanvas = new Canvas(screenshotBitmap);

        cropRect = new Rect(0,0,width,screenshotHeightPx);


    }

    @Override
    public void onScrollChanged() {
        if(listener == null) return;

        Bitmap bitmap = getDrawingCache();

        screenshotCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

        screenshotCanvas.drawBitmap(bitmap,cropRect, cropRect,paint);
        listener.onNewScreenshot(screenshotBitmap);
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.lelloman.screenshottablescrollview.MainActivity">

    <com.example.lelloman.screenshottablescrollview.ScreenshottableScrollView
        android:id="@+id/scrollView"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="0dp">

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    </com.example.lelloman.screenshottablescrollview.ScreenshottableScrollView>
    <View
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:background="#ff000000"/>
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="100dp" />

</LinearLayout>

MainActivity.java

package com.example.lelloman.screenshottablescrollview;

import android.graphics.Bitmap;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.Random;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        StringBuilder builder = new StringBuilder();

        Random random = new Random();
        String AB = "abcdefghijklmnopqrstuvwxyz ";

        for(int i=0;i<100;i++){
            builder.append("\n\n"+Integer.toString(i)+"\n\n");
            for(int j =0;j<1000;j++){
                builder.append(AB.charAt(random.nextInt(AB.length())));
            }
        }

        ((TextView) findViewById(R.id.textView)).setText(builder.toString());
        final ImageView imageView = (ImageView) findViewById(R.id.imageView);

        ScreenshottableScrollView scrollView = (ScreenshottableScrollView) findViewById(R.id.scrollView);
        scrollView.setScreenshotHeightPx((int) (getResources().getDisplayMetrics().density * 100));

        scrollView.setOnNewScreenshotListener(new ScreenshottableScrollView.OnNewScreenshotListener() {
            @Override
            public void onNewScreenshot(Bitmap bitmap) {
                Log.d("MainActivity","onNewScreenshot");
                imageView.setImageBitmap(bitmap);
            }
        });
    }
}