Как увеличить/увеличить часть изображения

Я создаю приложение, в котором пользователь сможет щелкнуть часть изображения и получить увеличенную версию в углу WebView. Мне удалось создать Paint, который сделал бы версию масштабирования, но отображает неправильное местоположение, например, некоторое смещение.

Я знаю, что этот вопрос задан много раз и уже был дан ответ, но, похоже, это не помогло тем решениям.

Здесь код, который я использовал:

  @Override
        public boolean onTouchEvent(@NonNull MotionEvent event) {
            zoomPos = new PointF();
            zoomPos.x = event.getX();
            zoomPos.y = event.getY();


        matrix = new Matrix();
        mShader = new BitmapShader(MainActivity.mutableBitmap, TileMode.CLAMP, TileMode.CLAMP);
        mPaint = new Paint();
        mPaint.setShader(mShader);
        outlinePaint = new Paint(Color.BLACK);
        outlinePaint.setStyle(Paint.Style.STROKE);

        int action = event.getAction(); 

        switch (action) { 
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
            zooming = true;
            this.invalidate();
            break; 
        case MotionEvent.ACTION_UP: 
            Point1 = true;
            zooming = false;
            this.invalidate();
            break;
        case MotionEvent.ACTION_CANCEL:
            zooming = false;
            this.invalidate();
            break; 

        default: 
            break; 
        }



     return true;
    }

    @Override
    protected void onDraw(@NonNull Canvas canvas) {
        super.onDraw(canvas);
        if (zooming) {
            matrix.reset();
            matrix.postScale(2f, 2f, zoomPos.x, zoomPos.y);
            mPaint.getShader().setLocalMatrix(matrix);


            canvas.drawCircle(100, 100, 100, mPaint);

        }

    }

Технически он должен нарисовать круг в верхнем левом углу и отобразить увеличенное изображение области, где мой палец, он рисует круг, но опять же, масштаб смещается.

Конечный результат должен выглядеть примерно так:

введите описание изображения здесь

MainActivity.java

public class MainActivity extends Activity {
static ImageView takenPhoto;
static PointF zoomPos;
Paint shaderPaint;
static BitmapShader mShader;
BitmapShader shader;
Bitmap bmp;
static Bitmap mutableBitmap;
static Matrix matrix;
Canvas canvas;
static Paint mPaint;
static Paint Paint;
static boolean zooming;


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

        File file = new File(Environment.getExternalStorageDirectory() + "/Pictures/boxes.jpg");

        String fileString = file.getPath();
        takenPhoto = (ZoomView) findViewById(R.id.imageView1);
        bmp = BitmapFactory.decodeFile(fileString);
        mutableBitmap = bmp.copy(Bitmap.Config.ARGB_8888, true);
        takenPhoto.setImageBitmap(mutableBitmap);    
        matrix = new Matrix();
        mShader = new BitmapShader(mutableBitmap, TileMode.CLAMP, TileMode.CLAMP); 
        mPaint = new Paint();
        mPaint.setShader(mShader);
        zoomPos = new PointF();
        Paint = new Paint(Color.RED);  
    }
}

ZoomView.java

public class ZoomView extends ImageView {

    private PointF zoomPos;
    PointF fingerPos;
    private Paint paint = new Paint(Color.BLACK);
    boolean zooming;
    Matrix matrix;
    BitmapShader mShader;
    Paint mPaint;
    Paint outlinePaint;
    boolean Point1;

    public ZoomView(Context context) {
        super(context);
    }

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

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

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        zoomPos = new PointF();
        zoomPos.x = event.getX();
        zoomPos.y = event.getY();


        matrix = new Matrix();
        mShader = new BitmapShader(MainActivity.mutableBitmap, TileMode.CLAMP, TileMode.CLAMP);
        mPaint = new Paint();
        mPaint.setShader(mShader);
        outlinePaint = new Paint(Color.BLACK);
        outlinePaint.setStyle(Paint.Style.STROKE);

        int action = event.getAction(); 

        switch (action) { 
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
            zooming = true;
            this.invalidate();
            break; 
        case MotionEvent.ACTION_UP: 
            Point1 = true;
            zooming = false;
            this.invalidate();
            break;
        case MotionEvent.ACTION_CANCEL:
            zooming = false;
            this.invalidate();
            break; 

        default: 
            break; 
        }



     return true;
    }

    @Override
    protected void onDraw(@NonNull Canvas canvas) {
        super.onDraw(canvas);
        if (zooming) {
            matrix.reset();
        matrix.postScale(2f, 2f, zoomPos.x, zoomPos.y);
        mPaint.getShader().setLocalMatrix(matrix);
        RectF src = new RectF(zoomPos.x-50, zoomPos.y-50, zoomPos.x+50, zoomPos.y+50);
        RectF dst = new RectF(0, 0, 100, 100);
        matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
        matrix.postScale(2f, 2f);
        mPaint.getShader().setLocalMatrix(matrix);


        canvas.drawCircle(100, 100, 100, mPaint);
        canvas.drawCircle(zoomPos.x, zoomPos.y, 100, mPaint);
        canvas.drawCircle(zoomPos.x-110, zoomPos.y-110, 10, outlinePaint);

        }
        if(Point1){
            canvas.drawCircle(zoomPos.x, zoomPos.y, 10, paint);
        }
    }
}

EDIT:

Как вы можете видеть, новый код намного лучше, все же есть некоторое смещение - черная точка - позиция пальца.

введите описание изображения здесь

Ответ 1

Кажется, проблема связана с тем, как вы используете matrix.

Теперь вы используете исходное изображение (1) как шейдер, который затем пост масштабируется вокруг точки опоры (2), что похоже на увеличение масштаба (3), но не центрирование точки (4)! (Например, открывайте карты Google и увеличивайте масштаб на карте с помощью мыши - точка масштабируется вокруг оси, но ось не центрирована).

введите описание изображения здесь

Каким будет более простой способ достичь того, чего вы хотите, с помощью Rect - Rect метод. И.Е. вы хотите взять небольшую область от исходного изображения (5) и нарисовать ее в большую область (6).

введите описание изображения здесь

И вот пример кода:

RectF src = new RectF(zoomPos.x-50, zoomPos.y-50, zoomPos.x+50, zoomPos.y+50);
RectF dst = new RectF(0, 0, 200, 200);
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);

Еще несколько пунктов:

  • Старайтесь не создавать объект new в onTouch - он вызывается много раз, и это не хорошо при производительности . Вместо этого создайте один раз и повторно используйте.
  • getAction() будут возникать проблемы, если на экране больше одного пальца, так как это идентификатор действия и идентификатор указателя. Вместо этого используйте getActionMasked() и getActionIndex().
  • Не используйте жестко заданные значения (50/100 в примере кода) - это пиксели и не знают о плотности экрана. Используйте масштабированный размер, например dp.