簡體   English   中英

如何找到圖像視圖上觸摸的所有像素?

[英]How to find all pixels touched on an image view?

在我的應用程序上,我有一個ImageView,我將其轉換為可編輯的位圖。 我需要檢測用戶觸摸了ImageView上的哪些像素。 此外,如果用戶用手指畫一條線,我需要知道所有被觸摸過的像素才能進行更改。 如何檢測觸摸了哪些像素?

好的,喬納,這是您的一些指示。
我想您想讓混合效果對用戶輸入快速做出反應,所以第一件事最好是使用自定義SurfaceView而不是ImageView,因為它更適合繪制2D動作游戲和動畫中所需的高幀頻動畫。 我強烈建議您閱讀本指南 在繼續之前,請特別注意有關SurfaceView的使用。 基本上,您將需要創建一個擴展SurfaceView並實現SurfaceHolder.Callback 然后,此視圖將負責偵聽用戶的觸摸事件並渲染幀以動畫化混合效果。
請看以下代碼作為參考:

    public class MainView extends SurfaceView implements SurfaceHolder.Callback {
        public MainView(Context context, AttributeSet attrs) {
            super(context, attrs);
            SurfaceHolder holder = getHolder(); 
            holder.addCallback(this);        // Register this view as a surface change listener
            setFocusable(true);              // make sure we get key events
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event);

            // Check if the touch pointer is the one you want
            if (event.getPointerId(event.getActionIndex()) == 0) {
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // User touched screen...
                case MotionEvent.ACTION_CANCEL:
                    // User dragged his finger out of the view bounds...
                case MotionEvent.ACTION_UP:
                    // User raised his finger...
                case MotionEvent.ACTION_MOVE:
                    // User dragged his finger...

                // Update the blending effect bitmap here and trigger a frame redraw,
                // if you don't already have an animation thread to do it for you.

                return true;
            }

            return false;
        }

        /*
         * Callback invoked when the Surface has been created and is ready to be
         * used.
         */
        public void surfaceCreated(SurfaceHolder holder) {
            // You need to wait for this call back before attempting to draw
        }

        /*
         * Callback invoked when the Surface has been destroyed and must no longer
         * be touched. WARNING: after this method returns, the Surface/Canvas must
         * never be touched again!
         */
        public void surfaceDestroyed(SurfaceHolder holder) {
            // You shouldn't draw to this surface after this method has been called
        }
    }

然后在“繪圖”活動的布局上使用它,如下所示:

    <com.xxx.yyy.MainView
        android:id="@+id/main_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

要繪制此表面,您需要以下代碼:

        Canvas c = null;

        try {
            c = mSurfaceHolder.lockCanvas(null);

            synchronized (mSurfaceHolder) {
                if (c != null)
                    c.drawBitmap(blendingImage, 0, 0, null);   // Render blending effect
            }
        } catch (Exception e) {
            Log.e("SurfaceView", "Error drawing frame", e);
        } finally {
            // do this in a finally so that if an exception is thrown
            // during the above, we don't leave the Surface in an
            // inconsistent state
            if (c != null) {
                mSurfaceHolder.unlockCanvasAndPost(c);
            }
        }

不能提供完整功能的示例來回答問題,因此建議您從Google下載Lunar Lander示例游戲以獲取完整的示例。 但是請注意,您不需要游戲動畫線程(盡管擁有一個動畫線程也不會受到傷害),就像Lunar Lander示例中編碼的那樣,如果您需要的只是混合效果。 該線程的目的是創建一個游戲循環,在該循環中不斷生成游戲框架以對可能依賴或不依賴用戶輸入的對象進行動畫處理。 在您的情況下,您需要做的就是在處理每個觸摸事件后觸發框架重繪。

編輯:下面的代碼是修復程序,以獲取您在注釋中提供的代碼,可以正常工作。
這是對MainActivity的更改:

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

    // put pics from drawables to Bitmaps
    Resources res = getResources();
    BitmapDrawable bd1 = (BitmapDrawable) res.getDrawable(R.drawable.pic1);

    // FIX: This block makes `operation` a mutable bitmap version of the loaded resource
    // This is required because immutable bitmaps can't be changed
    Bitmap tmp = bd1.getBitmap();
    operation = Bitmap.createBitmap(tmp.getWidth(), tmp.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(operation);
    Paint paint = new Paint();
    c.drawBitmap(tmp, 0f, 0f, paint);

    BitmapDrawable bd2 = (BitmapDrawable) res.getDrawable(R.drawable.pic2);
    bmp = bd2.getBitmap();

    myView = new MainView(this, operation, bmp);
    FrameLayout preview = (FrameLayout) findViewById(R.id.preview);
    preview.addView(myView);

}
...

這是對MainView類的更改:

public class MainView extends SurfaceView implements Callback {

    private SurfaceHolder holder;
    private Bitmap operation;
    private Bitmap bmp2;
    private boolean surfaceReady;

    // took out AttributeSet attrs
    public MainView(Context context, Bitmap operation, Bitmap bmp2) {
        super(context);

        this.operation = operation;
        this.bmp2 = bmp2;

        holder = getHolder();     // Fix: proper reference the instance variable
        holder.addCallback(this); // Register this view as a surface change
                                    // listener
        setFocusable(true); // make sure we get key events
    }

    // Added so the blending operation is made in one place so it can be more easily upgraded
    private void blend(int x, int y) {
        if (x >= 0 && y >= 0 && x < bmp2.getWidth() && x < operation.getWidth() && y < bmp2.getHeight() && y < operation.getHeight())
            operation.setPixel(x, y, bmp2.getPixel(x, y));
    }

    // Added so the drawing is now made in one place
    private void drawOverlays() {
        Canvas c = null;
        try {
            c = holder.lockCanvas(null);
            synchronized (holder) {
                if (c != null)
                    c.drawBitmap(operation, 0, 0, null); // Render blending
                                                            // effect
            }
        } catch (Exception e) {
            Log.e("SurfaceView", "Error drawing frame", e);
        } finally {
            // do this in a finally so that if an exception is thrown
            // during the above, we don't leave the Surface in an
            // inconsistent state
            if (c != null) {
                holder.unlockCanvasAndPost(c);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);

        if (!surfaceReady)     // No attempt to blend or draw while surface isn't ready
            return false;

        // Check if the touch pointer is the one you want
        if (event.getPointerId(event.getActionIndex()) == 0) {
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // User touched screen. Falls through ACTION_MOVE once there is no break

            case MotionEvent.ACTION_MOVE:
                // User dragged his finger...
                blend((int) event.getX(), (int) event.getY());

            }
            // Update the blending effect bitmap here and trigger a frame
            // redraw,
            // if you don't already have an animation thread to do it for you.
            drawOverlays();
            return true;
        }

        return false;
    }

    /*
     * Callback invoked when the Surface has been created and is ready to be
     * used.
     */
    public void surfaceCreated(SurfaceHolder holder) {
        surfaceReady = true;
        drawOverlays();
    }

    /*
     * Callback invoked when the Surface has been destroyed and must no longer
     * be touched. WARNING: after this method returns, the Surface/Canvas must
     * never be touched again!
     */
    public void surfaceDestroyed(SurfaceHolder holder) {
        // You shouldn't draw to this surface after this method has been called
        surfaceReady = false;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        // TODO Auto-generated method stub
    }

}

該代碼對我有用。 我只是希望我不會忘記任何事情= :)
讓我知道您是否仍然遇到麻煩,好嗎?

因此,答案是您必須要聰明一點,但事實並非如此。 除了在此處發布所有代碼以完成您想要的工作之外,我在這里為您提供鏈接和說明。

因此,通過管理應用程序的觸摸事件,您可以算出觸摸事件的平均坐標。 使用該信息,您可以確定觸摸的所有像素的中心,並在用手指畫一條線時繼續跟蹤。 要跟蹤直線,請使用

case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:

子句確定行的開始和結束。 如果要跟蹤不直線且未繪制的線,則需要使用

case MotionEvent.ACTION_MOVE:

那應該可以讓您開始。 您可能需要一點邏輯來處理這樣的事實,即您會畫一條很細的線,我懷疑這並不是您想要的。 也許是這樣。 無論哪種方式,這都應該是一個入門的好地方。

編輯

關於您的第一條評論, 是您可以使用的鏈接示例。 不過,我必須作一個小的聲明。 為了使所有部分正確地協同工作,乍一看似乎並不那么簡單。 我向您保證,這是最簡單的示例之一,並將本教程分為幾節。

對於您想做的事情,我想您需要特別注意第2節(不要與第2步混淆):

 2. Facilitate Drawing

我建議這樣做,因為它顯示了使用TouchEvent來使用信息的不同方法。 第1節中的內容將稍微解釋一下設置顯示TouchEvent捕獲數據的環境,而第3節中的大部分內容是美學。 這可能無法直接回答您的問題,但是我懷疑它將帶您到達需要的地方。

編碼愉快! 如有任何疑問,請發表評論。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM