Phương thức onTouchEvent(MotionEvent event)

Một phần của tài liệu ứng dụng tô màu cho bé (Trang 44)

Mỗi hành động vẽ của ta sẽ bắt đầu khi ta chạm tay vào màn hình, di chuyển trên màn hình và kết thúc khi ta nhấc tay lên. Vì thế tương ứng với ba hành động đó ta cần xây dựng các phương thức phù hợp để thực hiện những công việc ta cần làm. Trong lớp MyView ta cần ghi đè lại phương thức onTouchEvent(MotionEvent event),

phương thức này sẽ bắt các sự kiện khi tay ta chạm vào màn hình : @Override

public boolean onTouchEvent(MotionEvent event) { switch (event.getAction())

{

case MotionEvent.ACTION_DOWN:

// các công việc cần thực hiện khi tay ta chạm vào màn hình

break;

case MotionEvent.ACTION_MOVE:

// các công việc cần thực hiện khi tay ta di chuyển trên màn hình

break;

case MotionEvent.ACTION_UP:

//các công việc cần thực hiện khi tay ta nhấc lên khỏi màn hình

break; }

return true; }

Ứng với mỗi hành động trên, có thể có nhiều công việc để lựa chọn thực hiện vì thế ta không thể đưa trực tiếp vào phương thức trên vì sẽ gây ra khó khăn trong việc quản lý và sửa đổi. Vì thế ta sẽ tạo thêm ba phương thức :

 private void touch_start(float x, float y): thực hiện các công việc khi chạm tay vào màn hình

 private void touch_move(float x, float y): thực hiện các công việc khi di chuyển tay trên màn hình

 private void touch_up():thực hiện các công việc khi nhấc tay lên khỏi màn hình

Khi chạm tay vào màn hình hoặc khi tay ta di chuyển trên màn hình ta cần xác định được tọa độ của điểm tay ta đang chạm vào, việc xác định tọa độ này nhằm giúp cho ta vẽ các hình cần thiết lên ngay điểm ta chạm vào, chính vì thế trong các phương thức touch_start() và touch_move() ta cần truyền vào 2 tham số float x, float y. Để xác định được tọa độ điểm ta đang chạm vào trong phương thức onTouchEvent(MotionEvent event)ta gọi phương thức getX(), getY() của tham số event:

 float x = event.getX();  float y = event.getY();

Như đã đề cập bên trên, mỗi khi ta vẽ một cái gì đó và muốn hiển thị lên màn hình thì ta phải gọi lại phương thức onDraw(Canvas canvas), để gọi phương thức này ta dùng phương thức invalidate()

@Override

public boolean onTouchEvent(MotionEvent event) { float x = event.getX();

float y = event.getY(); switch (event.getAction()) {

case MotionEvent.ACTION_DOWN: touch_start(x,y);

invalidate(); break;

case MotionEvent.ACTION_MOVE: touch_start(x,y);

invalidate(); break;

case MotionEvent.ACTION_UP: touch_start(x,y); invalidate(); break; } return true; }

CHƯƠNG III: NỘI DUNG KẾT QUẢ NGHIÊN CỨU 3.1 PHÁT TRIỂN ỨNG DỤNG 3.1.1 Sơ đồ chức năng Hình 8: Sơ đồ chức năng BabyPaint Chọn hình kèm theo ứng dụng Xóa Lựa chọn nét vẽ, hình dạng muốn vẽ Thay đổi độ lớn nét vẽ Chọn cách đổ màu Phục hồi Chọn hình từ máy Chia sẻ Tạo mới Lưu Thay đổi màu sắc

nét vẽ Phát âm tiếng anh Tải hình ảnh lên bức vẽ Chọn nét vẽ Chọn hình dạng vẽ Nét bình thường Nét nổi Nét mờ Nét đứt đoạn Nét có bóng đổ Nét sáp 1 Nét sáp 2

Hình chữ nhật, tam giác, đường thẳng, hình nhật bo tròn góc Hình tròn, trái tim, ngôi sao, oval

Hình nửa vầng trăng, tia chóp, mũi tên dọc, mũi tên ngang

Tô màu vào vùng giới hạn cùng màu Tô màu vào vùng có bán kính định trước

Nét vẽ hình ngôi sao Nét vẽ hình trái tim

3.1.2 Xây dựng giao diện

Với mục tiêu xây dựng một giao diện đơn giản, hiệu quả, đem lại sự thuận tiện cho người dùng. Giao diện chính của ứng dụng sẽ được bố trí như sau:

Hình 9 : Thiết kế giao diện

Như bố trí trên hình, toàn bộ giao diện của ứng dụng sẽ được bố trí trong một LinearLayout theo chiều dọc. Theo chiều từ trên xuống, đầu tiên là một HorizontalSrollView chứa đựng các ImageButton, các nút này sẽ tương ứng với các chức năng chính của ứng dụng. Các ImageButton trong HorizontalSrollView thứ 2 (nằm ở dòng dưới ) sẽ tương ứng với các màu cơ bản. ImageButton đầu tiên (tính từ trái qua) ở dòng thứ hai là nút để thực hiện chức năng phát âm, nút thứ hai sẽ thực hiện chức năng chọn màu. Ngoài ra, ta còn có một Gallery, tuy nhiên đối tượng này đã được ẩn đi, nó

LinearLayout: orientation=”vertical” FrameLayout HorizontalSrollView LinearLayout:orientation=”horizontal” ImageButton ImageButton

chỉ được hiển thị khi ta thực hiện chức năng chọn hình kèm theo ứng dụng. Chính giữa màn hình là một FrameLayout, đây là nơi để ta thực hiện vẽ lên màn hình. Giao diện của ứng dụng sẽ được định nghĩa trong một tập tin XML để dễ cho việc sửa đổi và chỉnh sửa sau này.Sau khi hoàn thành ứng dụng có giao diện như sau :

Hình 10: Giao diện sau khi hoàn thành

3.1.3 Chức năng chọn nét vẽ, hình dạng muốn vẽ

Như đã đề cập ở trên, để có thể vẽ lên được màn hình ta cần xây dựng một lớp con của lớp View. Lớp này sẽ chứa đựng các đối tượng thuộc lớp Bitmap, Canvas, Path và Paint. Đồng thời trong lớp này cần phải ghi đè lại các phương thức onDraw(Canvas canvas) và onTouchEvent(MotionEvent event). Lớp này sẽ giữ vai trò chính cho việc vẽ, tô màu. Khi ta vẽ lên màn hình, lúc đó tay ta sẽ di chuyển trên màn hình vì thế các hành động vẽ sẽ được định nghĩa trong phương thức touch_move(float x, float y). Để biết được ta đang chọn nét vẽ nào thì ta cần khai báo một biến kiểu int , biến này sẽ được gán những giá trị khác nhau ứng với mỗi nét vẽ, và hành dạng muốn vẽ . Lúc đó,trong lớp MyView ta cũng cần định nghĩa thêm một phương thức để gán giá trị cho biến này :

public class MyView extends View {

int choose_number = 1;//chọn nét vẽ, hình dạng vẽ

public void setChooseNumber(int n) { choose_number = n;

}

private void touch_move(float x, float y) { float dx = Math.abs(x - mX);

float dy = Math.abs(y - mY); switch (choose_number) {

case 1:

//nét vẽ bình thường

paint.setMaskFilter(null); // hủy nét nổi và mờ

paint.setPathEffect(null);// hủy thiết lập nét vẽ đứt đoạn

paint.clearShadowLayer();// hủy thiết lập bóng đỗ

if (dx >= 4 || dy >= 4) {

path.quadTo(mX, mY, x, y); mX = x; mY = y; } break; case 2: //nét vẻ nổi paint.setPathEffect(null); paint.clearShadowLayer();

emboss = new EmbossMaskFilter(new float[] { 1, 1, 1 },0.4f, 6, 3.5f);

paint.setMaskFilter(emboss);//thiết lập nét vẽ nổi

if (dx >= 4 || dy >= 4) {

path.quadTo(mX, mY, (x + mX)/2, (y + mY)/2); mX = x; mY = y; } break; // . . . } //... }

Các nét còn lại ta làm tương tự như trên , chỉ khác nhau ở chỗ :

 Nét mờ : paint.setMaskFilter(new

BlurMaskFilter(10,Blur.INNER));

 Nét đứt đoạn :

PathEffect ef =new DashPathEffect(new float[] {20,10,5,10},0);

paint.setPathEffect(ef);

 Nét sáp 1 :

MyFill f = new MyFill();

f.fillCircle_gradient(bitmap, new

Point((int)x,(int)y), (int)(5+

paint.getStrokeWidth()), paint.getColor());

 Nét sáp 2 :

MyFill f = new MyFill();

f.fillCircle(bitmap, new Point((int)x,(int)y), (int)(5+ paint.getStrokeWidth()), paint.getColor()); Đối với việc lựa chọn hình dạng để vẽ , ta cũng làm tương tự như trên :

//...

case number: {

path.reset();//xóa các hình dạng trước đó // phương thức vẽ hình chữ nhật,

path.addRect(mX, mY, x, y,Path.Direction.CW);

break; }

//...

Đối với một ứng dụng Android, bao giờ ta cũng có một lớp MainActivity( tên này có thể thay đổi do người lập trình đặt), đối với ứng dụng của ta lớp này có tên là BabyPaintActivity, lớp này là lớp chính của ứng dụng, mọi chức năng điều được thực hiện thông qua lớp này. Ta cần tạo một đối tượng thuộc lớp MyView đã được xây dựng, việc vẽ,tô màu sẽ thực thông qua đối tượng này, để thể hiện đối tượng này lên màn hình (thể hiện lên FrameLayout đã được thiết kế trong phần giao diện) ta cần thêm đối tượng này vào đối tượng thuộc lớp FrameLayout liên kết với FrameLayout đã khai báo trong tập tin XML:

Trong tập tin XML định nghĩa giao diện : // … <FrameLayout android:id="@+id/display" android:layout_width="match_parent" android:layout_height="match_parent" > </FrameLayout> // … Trong lớp BabyPaintActivity : @Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);//không hiển thị tiêu đề

mview = new MyView(this);

display = (FrameLayout)findViewById(R.id.display); display.addView(mview);

// … }

Trong ứng dụng, để thực hiện việc chọn nét vẽ, hình dạng hình cần vẽ ta sẽ nhấn vào một nút tương ứng với chức năng này. Khi nhấn vào nút này, một dialog sẽ xuất hiện, trên đó sẽ có những nút tương ứng với nét vẽ, hình dạng ta cần chọn. Dialog này được thiết kế trong một lớp riêng, trong lớp này có một phương thức với một tham số kiểu MyView. Thông qua tham số này, ta sẽ xác định được được nét vẽ hoặc hình dạng muốn vẽ thông qua tham số setChooseNumber(int n)như đã thiết kế ở phần trên.

public class ShapeDialog { // …

public void ChooseShape(final MyView mv) {

final Dialog da = new Dialog(context); da.setContentView(R.layout.shape_dialog); da.setTitle("Choose shape");

// …

OnClickListener listener = new OnClickListener() {

public void onClick(View v) {

// TODO Auto-generated method stub

switch(v.getId()){

case R.id.bt_pencil:{

mv.setChooseNumber(1); da.dismiss();

break; }

case R.id.bt_tron:{

mv.setChooseNumber(2); da.dismiss(); break; } //… } // … }

Để hiển thị được dialog này, trong lớp BabyPaintActivity ta cần thiết kế một phương thức cho sự kiện onClick() của nút tương ứng với chức năng chọn nét vẽ, hình dạng cần vẽ :

public void ChooseShape(View v) {

da_shape.ChooseShape(mview); }

Khi đó trong tập tin XML định nghĩa giao diện, tại nút (ImageButton ) tương ứng chức năng này ta thêm thuộc tính android:onClick="ChooseShape"

// ... <ImageButton android:id="@+id/bt_choose_shape" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon_pen" android:onClick="ChooseShape"/> // …

Kết quả sau khi ứng dụng hoàn thành :

Hình 11: Dialog chọn nét vẽ

3.1.4 Chức năng thay đổi nét độ lớn nét vẽ

Trong ứng dụng, độ lớn nét vẽ được thay đổi thông qua đối tượng paint được khai báo trong lớp MyView chính vì thế để thay đổi được độ lớn nét vẽ , trong lớp MyView ta cần xây dựng một phương thức để thực hiện việc thay đổi này :

public void setMySize(int s) { paint.setStrokeWidth(s); }

Tương tự như chắc năng thay đổi nét vẽ, trên giao diện của ứng dụng ta cũng cần có một nút, khi bấm vào nút này màn hình sẽ hiện thị một dialog trên đó cho phép ta lựa chọn độ lớn của nét vẽ. Chính vì thế ta cần xây dựng một lớp riêng(class

BrushDialog) để hiển thị dialog này. Trên dialog này ta sẽ có một ListView để liệt kê các giá trị phổ biến, bên cạnh đó sẽ có một SeekBar để lựa chọn giá trị tùy ý, một TextView hiển thị giá trị hiện tại đang chọn(hiển thị số) , một ImageButton để chấp nhận giá trị lựa chọn và một ImageView thể hiện hình ảnh nét vẽ khi ta thay đổi giá trị của SeekBar

Khác với chức năng chọn nét vẽ một chút, trong lớp dialog hiển thị độ lớn nét vẽ, ta cần xây dựng một phương thức với một tham số là đối tượng của lớp MyView và một tham số kiểu int. Tham số kiểu MyView được dùng để xác định độ lớn nét vẽ, còn tham số kiểu int dùng để xác định độ lớn nét vẽ hiện tại.

public class BrushDialog { //…

public void chooseBrush(final MyView mv, int p) {

final Dialog da = new Dialog(context); da.setContentView(R.layout.brush_dialog); da.setTitle("Choose size");

//… } // …

}

Trong lớp trên ta cần bắt các sự kiện quan trọng như khi SeekBar thay đổi hoặc khi các Item trên ListView được nhấn vào

public class BrushDialog { //…

public void chooseBrush(final MyView mv, int p) {

final Dialog da = new Dialog(context); da.setContentView(R.layout.brush_dialog); da.setTitle("Choose size");

//…

//Bắt sự kiện onProgressChanged trên đối tượng seekbar OnSeekBarChangeListener seekbarlistener = new

OnSeekBarChangeListener(){

public void onProgressChanged(SeekBar seekbar, int progress, boolean arg2) {

switch(seekbar.getId()){ case R.id.seekBar1:

{

int value = progress+1; text.setText(""+value); px=value; break; } } }; seekbar.setOnSeekBarChangeListener(seekbarlistener); //… } //Bắt sự kiện onItemClick listview.setOnItemClickListener(new OnItemClickListener(){

public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {

// TODO Auto-generated method stub if(position == 0){ mv.setMySize(5); da.dismiss(); } // … } }); // … }

Trong lớp BabyPaintActivity ta cũng cần định nghĩa một phương thức để hiển thị dialog này lên :

public void ChooseBrush(View v) {

da_brush.chooseBrush(mview,(int)mview.paint.getStrokeWidth( ));

//da_brush là một đối tượng thuộc lớp BrushDialog }

Phương thức này được gọi khi ta nhấn vào nút tương ứng với chức năng này : <ImageButton

android:id="@+id/bt_choose_shape"

android:layout_height="wrap_content"

android:src="@drawable/icon_pen"

android:onClick="ChooseBrush"/>

Kết quả sau khi ứng dụng hoàn thành :

Hình 12: Dialog chọn độ lớn nét vẽ

3.1.5 Chức thay đổi màu sắc nét vẽ

Tương tự như chức năng thay đổi độ lớn nét vẽ, muốn thay đổi màu của nét vẽ ta cùng phải thông qua đối tượng paint của lớp MyView vì thế trong lớp này ta cần định nghĩa phương thức sau :

public void setMyColor(int c) { paint.setColor(c);

}

Màu sắc của nét vẽ có thể thay đổi thông qua việc nhấn vào các nút màu tương ứng trên giao diện. Tuy nhiên trên giao diện này chỉ có những màu cơ bản, để có thể lựa chọn nhiều màu sắc hơn, ta sẽ xây dựng một lớp riêng để hiển thị một dialog chọn màu, . Để chọn màu từ các nút màu cơ bản trên giao diện, ứng với mỗi nút này ta cần định nghĩa các phương thức tương ứng để thay đổi màu.

public void Green(View v) {

mview.setMyColor(Color.GREEN); temColor = Color.GREEN;

}

Phương thức này được gọi ứng với nút màu tương ứng : <ImageButton android:id="@+id/imageButton6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/green" android:onClick="Green"/>

Dialog chọn màu cũng được thế kế trong một lớp riêng (lớp ColorDialog) , trong lớp này cũng cần định nghĩa một phương thức để hiển thị dialog này lên màn hình. Phương thức này sẽ có 2 tham số truyền vào : 1 tham số là đối tượng của lớp MyView để gán màu thông qua tham số này, 1 tham số kiểu int để lưu giữ màu hiện tại. Trên dialog này ta có các thành phần chính: 1 ImageView để hiển thị vành khuyên chọn màu, 1 button để hiển thị màu hiện tại, 1 button để hiển thị màu đang lựa chọn , 1 ImageView hiển thị thanh màu chuyển từ đậm tới nhạt tương ứng với màu chọn trên vành khuyên.

Trong phương thức này ta cần khai báo một đối tượng thuộc lớp Bitmap, đây là nơi ta vẽ hình vành khuyên lên và hiển thị lên màn hình thông qua đối tượng ImageView . Để vẽ được lên đối tượng Bitmap đã khai báo ta cần khai báo thêm một đối tượng kiểu Paint và Canvas. Ta cần ghi đè lại phương thức onTouch(View v, MotionEvent event) để thực hiện đổi màu khi tay ta di chuyển trên vành khuyên .

Bố cục các thành phần trên dialog : tạo file color.xml với bố cục như hình bên dưới

Hình 13: Thiết kế giao diện của dialog chọn màu Tiêu đề LinearLayout: orientation=” vertical” ImageView Màu muốn chọn LinearLayout: orientation=”h orizontal”

Màu hiện tại

ImageButton

Vành khuyên chọn màu

Thanh màu chuyển từ đậm sang nhạt

Kết quả sau khi hoàn thành :

Hình 14: Dialog chọn màu

3.1.6 Chức năng phục hồi

Đôi khi trong lúc người dùng tô hoặc vẽ có thể xảy ra sai sót và muốn khôi phục lại trạng thái trước đó, chức năng này sẽ giúp người dùng thực hiện điều đó . Chức năng này thực hiện theo nguyên tắc sau : trước khi người dùng tô, hoặc vẽ lên màn hình, trạng thái trước đó sẽ lưu trữ lại, đề phục hồi lại trạng thái trước đó thì ta chỉ cần vẽ lại trạng thái đã được lưu trữ lên màn hình. Trong ứng dụng ta vẽ lên đối tượng thuộc lớp Bitmap vì thế trước khi vẽ ta sẽ lưu trữ bitmap này lại, các bitmap sẽ được lưu trữ vào một danh sách theo thứ tự từ trước đến sau, các bitmap được lưu trữ sau sẽ được lưu vào cuối danh sách . Khi muốn phục hồi lại ta lấy bitmap cuối danh sách và vẽ lại lên màn hình. Một hành động vẽ bắt đầu khi ta chạm tay vào màn hình, vì thế việc lưu trữ các bitmap sẽ được thực hiện ngay khi ta chạm tay vào màn hình để lưu trữ lại trạng thái trước đó:

Khai báo danh sách lưu lại các bitmap :

List<Bitmap> undoBit = new ArrayList<Bitmap>(); Lưu lại các bitmap khi chạm tay vào màn hình :

private void touch_start(float x, float y) {

if(undoBit.size()==5)//chi undo duoc 5 lan truoc do { undoBit.remove(0); } undoBit.add(undoBit.size(), bitcp); }

Vì bộ nhớ thiết bị có hạn nên không thể lưu trữ lại nhiều bitmap được, nếu có có quá nhiều bitmap được lưu lại sẽ gây cạn kiệt bộ nhớ và gây ra lỗi chương trình .Chính vì thế, ở đây ta giới hạn lại số lần phục hồi trước đó chỉ là 5. Để thực hiện được điều này thì trước khi thêm bitmap vào danh sách, ta kiểm tra xem số phần tử trong danh sách có

Một phần của tài liệu ứng dụng tô màu cho bé (Trang 44)