[Android]Thiết lập vẽ tùy biến.

Trong nhiều ứng dụng trên điện thoại Android, chắc hẳn các bạn đều thấy có chức năng khá cơ bản đó là người dùng có thể vẽ tùy biến trên màn hình điện thoại Android. Vậy điều đó được làm như thế nào? Cách xử lý của chúng ra sao. Hôm nay tôi sẽ hướng dẫn các bạn cách cơ bản để làm được điều này. Trong bài viết này tôi sử dụng Android Studio để lập trình.

Khởi động dự án

Ta sẽ tạo mới project thông qua File/New/New Project, sau đó ta thiết lập tên là MakePaintingView như hình dưới Sau đó ta chọn phiên bản Android, ở đây tôi chọn Android 5 2 bước tiếp theo chọn class chạy đầu tiên và Thiết lập bạn đầu. Sau khi chọn xong ta sẽ có màn giao diện để code như sau: Sẽ có 2 file mặc định để code đó là MainActivity,java và file thiết lập giao diện là content_main.xml

package com.example.framgianguyenquanghuy.makepaintingview;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/activity_main" tools:context=".MainActivity">

    <TextView android:text="Hello World!" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

Thiết lập giao diện cho dự án

Để thiết lập giao diện cho dự án t cần chỉnh sửa file content_main.xml như sau

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".MainActivity" >

    <com.example.framgianguyenquanghuy.makepaintingview.TouchDrawingWithPath
        android:id="@+id/simpleDrawingView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true" />

</RelativeLayout>

Trong đó <com.example.framgianguyenquanghuy.makepaintingview.TouchDrawingWithPath sẽ là tên class mà ta quy định sử dụng trong các kiểu vẽ khác nhau.

Vẽ hình tròn cố định

Đầu tiên để làm quen ta thử vẽ những hình tròn ở những vị trí cố định. Ở đây ta vẽ 3 vòng tròn lần lượt màu là Đen, Xanh lá và Xanh nước biển. Cần tạo 1 class phần vẽ này như sau:

package com.example.framgianguyenquanghuy.makepaintingview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by FRAMGIA\nguyen.quang.huy on 12/10/2017.
 */
public class SimpleDrawingView extends View {
    // setup initial color
    private final int paintColor = Color.BLACK;
    // defines paint and canvas
    private Paint drawPaint;
    public SimpleDrawingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setFocusable(true);
        setFocusableInTouchMode(true);
        setupPaint();
    }

    // Setup paint with color and stroke styles
    private void setupPaint() {
        drawPaint = new Paint();
        drawPaint.setColor(paintColor);
        drawPaint.setAntiAlias(true);
        drawPaint.setStrokeWidth(5);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawCircle(50, 50, 20, drawPaint);
        drawPaint.setColor(Color.GREEN);
        canvas.drawCircle(50, 150, 20, drawPaint);
        drawPaint.setColor(Color.BLUE);
        canvas.drawCircle(50, 250, 20, drawPaint);
    }
}

Và kết quả ta được như sau: Do để full màn hình và có title nên bị mất hình tròn màu đen.

Vẽ theo tương tác ngươi dùng

Giờ ta sẽ vẽ theo tương tác người dùng lên màn hình. Để vẽ như vậy ta cần thêm sự kiện onTouchEvent để xử lý.

package com.example.framgianguyenquanghuy.makepaintingview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by FRAMGIA\nguyen.quang.huy on 12/10/2017.
 */
public class TouchDrawingView extends View {
    // setup initial color
    private final int paintColor = Color.BLACK;
    // defines paint and canvas
    private Paint drawPaint;
    // Store circles to draw each time the user touches down
    private List<Point> circlePoints;

    public TouchDrawingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setupPaint(); // same as before
        circlePoints = new ArrayList<Point>();
    }

    // Draw each circle onto the view
    @Override
    protected void onDraw(Canvas canvas) {
        for (Point p : circlePoints) {
            canvas.drawCircle(p.x, p.y, 5, drawPaint);
        }
    }

    // Append new circle each time user presses on screen
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float touchX = event.getX();
        float touchY = event.getY();
        circlePoints.add(new Point(Math.round(touchX), Math.round(touchY)));
        // indicate view should be redrawn
        postInvalidate();
        return true;
    }

    private void setupPaint() {
        drawPaint = new Paint();
        drawPaint.setColor(paintColor);
        drawPaint.setAntiAlias(true);
        drawPaint.setStrokeWidth(5);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
        drawPaint.setStyle(Paint.Style.FILL); // change to fill
        // ...
    }
}

Và kết quả ta thu về được là vẽ chủ động như sau:

Vẽ hình liên tục

Như ở trên thì việc vẽ theo tương tắc trên màn hình điện thoại thì nó là các hình tròn nối tiếp nhau, như vậy sẽ khiến hình bị rời rạc. Vậy để vẽ liên tục thì ta sẽ khai báo như sau: Ở đây ta sẽ thay sự kiện khi vẽ k phải là list nữa mà vẽ thành 1 đường luôn, và sửa lại đoạn sự kiện Ontouch

 public boolean onTouchEvent(MotionEvent event) {
        float pointX = event.getX();
        float pointY = event.getY();
        // Checks for the event that occurs
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // Starts a new line in the path
                path.moveTo(pointX, pointY);
                break;
            case MotionEvent.ACTION_MOVE:
                // Draws line between last point and this point
                path.lineTo(pointX, pointY);
                break;
            default:
                return false;
        }
package com.example.framgianguyenquanghuy.makepaintingview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by FRAMGIA\nguyen.quang.huy on 12/10/2017.
 */
public class TouchDrawingWithPath extends View{
    // setup initial color
    private final int paintColor = Color.BLACK;
    // defines paint and canvas
    private Paint drawPaint;
    // Store circles to draw each time the user touches down
    private List<Point> circlePoints;

    private Path path = new Path();
    public TouchDrawingWithPath(Context context, AttributeSet attrs) {
        super(context, attrs);
        setupPaint(); // same as before
        circlePoints = new ArrayList<Point>();
    }

    // Draw each circle onto the view
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawPath(path, drawPaint);
    }

    // Append new circle each time user presses on screen
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float pointX = event.getX();
        float pointY = event.getY();
        // Checks for the event that occurs
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // Starts a new line in the path
                path.moveTo(pointX, pointY);
                break;
            case MotionEvent.ACTION_MOVE:
                // Draws line between last point and this point
                path.lineTo(pointX, pointY);
                break;
            default:
                return false;
        }

        postInvalidate(); // Indicate view should be redrawn
        return true; // Indicate we've consumed the touch
    }

    private void setupPaint() {
        drawPaint = new Paint();
        drawPaint.setColor(paintColor);
        drawPaint.setAntiAlias(true);
        drawPaint.setStrokeWidth(5);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
        drawPaint.setStyle(Paint.Style.STROKE);
        // ...
    }
}

Và kết quả thu được như sau: Rõ ràng nếu viết chữ thì sẽ đẹp hơn. Tài liệu tham khảo: https://guides.codepath.com/


All Rights Reserved