【问题标题】:How to capture part of a ScrollView as its content is scrolling?如何在滚动内容时捕获 ScrollView 的一部分?
【发布时间】:2016-02-16 14:45:41
【问题描述】:

在扩展 ScrollView 类后,我能够轻松地实时收到滚动通知。

现在我需要在一个非常具体的部分捕获此滚动视图的内容。 假设我想捕捉屏幕的顶部(匹配父宽度和定义的高度,如 100dp)。但只有 ScrollView 的内容,而不是其余的,如果顶部还有其他内容但不是 ScrollView 的一部分。

我尝试在滚动视图上使用:

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

然后我尝试裁剪以获得我想要的部分:

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

结果与我想要实现的目标相差甚远,性能也非常差,有时我会得到SIGENVgetDrawingCache(true) 尝试使用回收的位图...

那么如何在不影响性能的情况下轻松捕获所需区域的内容?

注意:这个过程必须在我滚动内容的时候完成,所以在ScrollView的onScrollChanged(final int x, final int y)里面。

谢谢!

【问题讨论】:

    标签: android bitmap scrollview capture


    【解决方案1】:

    由于问题很有趣,我实现了它,它似乎工作正常。我猜你每次都在重新创建一个位图,这就是为什么会变慢。

    想法是这样的,您在 ScrollView 中创建一个要复制的区域(请参阅 Rect cropRectBitmap screenshotBitmap),它是全宽的,您只需要设置高度即可。视图会自动在其自身上设置一个滚动侦听器,并且在每次滚动时它都会复制该区域。请注意setDrawingCacheEanbled(true) 在视图被实例化时只被调用一次,它基本上告诉视图您将调用getDrawingCache(),这将返回视图在其上绘制自身的位图。然后它会在 screenshotBitmap 上复制感兴趣的区域,这就是您可能想要使用的位图。

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

    【讨论】:

    • 非常感谢!有用 !但是性能很差,滚动似乎很慢。但是,它似乎没有反映在 Android Studio 中的 GPU 监视器上,因为我从未走过保证恒定 60 FPS 的绿线(16 毫秒)。很奇怪,对吧?无论如何,有没有办法让我的滚动流畅?此外,如果我快速滚动,屏幕截图似乎太不同步了,这是一个遗憾。我希望我改进这个过程。非常感谢!!
    • 测试您的代码,似乎getDrawingCache(); 行是罪魁祸首......所以我想没有什么可做的? ://
    • 很高兴它有帮助,现在我将添加一些解释。关于性能,我不知道如何才能做得更好,但这也取决于您使用位图做什么。无论如何,如果它解决了您的问题,请考虑接受答案^^
    • 好的,但是捕获视图的背景呢?假设我希望捕获标题的背景(因此我的问题中提到了顶部)?比起捕获整个 ScrollView(整个屏幕),它不会让这个过程变得更容易和更快吗?甚至可能吗?我不确定这是否可能。这里的原因是我想在这个标题中应用一个过滤器。这意味着此标头后面的任何内容(在本例中为 ScrollView 内容)都必须应用过滤器。这就是我问这个问题的原因。你觉得我能做到这么多吗?
    • 好的 ^^ 我的 Activity 由两个元素组成:标题和 ScrollView。很明显,header 并没有移动,而是显示了与 ScrollView 的内容相关的信息。 ScrollView 包含数据,您可以滚动它。事情是 ScrollView 占据了整个屏幕,但标题仍然在上面。我应该在这个标题后面应用一个过滤器,这意味着它将应用于标题后面的 ScrollView 内容。所以我的问题是:我可以只捕获标题后面的内容而不必捕获整个 ScrollView(屏幕)然后裁剪它吗?谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多