Android 白板应用:拖动与放大画布

在现代应用程序中,白板功能越来越受到欢迎,尤其是在教育、培训和远程协作场景中。Android 平台为开发各种类型的应用提供了丰富的 API。本文将探讨如何在 Android 平台上实现一个简单的白板应用,支持画布的拖动和放大。我们将分步骤进行讲解,并用代码示例进行说明。

1. 项目结构

在开始编写代码之前,让我们先了解一下我们的项目结构。我们的 Android 白板应用将包含以下几个核心部分:

  • MainActivity.java:主活动,用于显示白板。
  • DrawingView.java:自定义视图,用于绘制和手势处理。
  • CanvasZoomHandler.java:用于处理画布的缩放和拖动。

状态图

为了更好地理解应用的工作流程,我们可以使用状态图来表示。在白板应用中,用户可以在不同的状态间切换,比如绘制状态、拖动状态和缩放状态。

stateDiagram
    [*] --> Idle
    Idle --> Drawing: Start drawing
    Idle --> Pan: Start panning
    Drawing --> Idle: Stop drawing
    Pan --> Idle: Stop panning
    Drawing --> Pan: Activate panning
    Pan --> Drawing: Activate drawing

2. 创建自定义绘图视图

接下来,我们需要创建一个自定义视图 DrawingView,用于处理用户的绘图、拖动和缩放手势。

示例代码:DrawingView.java

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

public class DrawingView extends View {

    private Paint paint;
    private Path path;
    private Bitmap bitmap;
    private Canvas canvas;

    private float scaleFactor = 1.0f;
    private ScaleGestureDetector scaleGestureDetector;

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

    private void init() {
        paint = new Paint();
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);

        path = new Path();
        scaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        canvas = new Canvas(bitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        canvas.scale(scaleFactor, scaleFactor);
        canvas.drawBitmap(bitmap, 0, 0, null);
        canvas.drawPath(path, paint);
        canvas.restore();
    }

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

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                path.moveTo(event.getX() / scaleFactor, event.getY() / scaleFactor);
                return true;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(event.getX() / scaleFactor, event.getY() / scaleFactor);
                break;
            case MotionEvent.ACTION_UP:
                canvas.drawPath(path, paint);
                path.reset();
                break;
        }
        invalidate();
        return true;
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            scaleFactor *= detector.getScaleFactor();
            scaleFactor = Math.max(0.1f, Math.min(scaleFactor, 5.0f));
            invalidate();
            return true;
        }
    }
}

在这个自定义视图中,我们使用 Path 来处理用户的绘图路径。ScaleGestureDetector 用于处理缩放手势,通过调整 scaleFactor 来实现画布的放大和缩小。

3. 主活动

现在我们有了绘图视图,接下来在 MainActivity 中引入该视图。

示例代码:MainActivity.java

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DrawingView drawingView = findViewById(R.id.drawing_view);
    }
}

同时,在 activity_main.xml 布局文件中引入 DrawingView

示例代码:activity_main.xml

<RelativeLayout xmlns:android="
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <com.example.app.DrawingView
        android:id="@+id/drawing_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

4. 关系图

在我们的应用中,主要的类之间的关系可以用 ER 图来表示。我们将 MainActivityDrawingViewCanvasZoomHandler 之间的关系以 ER 图展示如下:

erDiagram
    MAINACTIVITY {
        String title
        void onCreate(Bundle savedInstanceState)
    }
    DRAWINGVIEW {
        Paint paint
        Path path
        Bitmap bitmap
        Canvas canvas
        float scaleFactor
    }
    MAINACTIVITY ||--o{ DRAWINGVIEW : contains

结论

通过以上的步骤,我们实现了一个简单的 Android 白板应用,具备了基本的绘图、拖动和缩放功能。这对于用户在各种场景中的需求都提供了有力的支持。在实际应用中,我们可以根据需求对该功能进行扩展,例如添加颜色选择、橡皮擦功能等。希望这篇文章对你理解 Android 白板应用的开发有所帮助,也希望你能够动手实践,尝试扩展更多的功能!