2.5.1 Tổng quan
Tất cả các giao diện người dùng trong Android đều được xây dựng bởi đối tượng View và ViewGroup . View là một đối tượng vẽ một cái gì đó lên màn hình mà người dùng có thể tương tác được . ViewGroup là đối tượng chứa đựng đối tượng View( và đối tượng ViewGruop) để định nghĩa bố cục giao diện người dùng
2.5.2 Bố cục giao diện người dùng
Giao diện người dùng cho mỗi thành phần của ứng dụng được xác định bởi hệ thống có cấp bậc của đối tượng View và ViewGroup, được thể hiện như hình bên dưới. Mỗi ViewGroup là một thành phần không nhìn thấy chứa đựng các đối
tượng con là các view, trong khi đó các view có thể là các điều khiển nhập hoặc là các widgets khác mà nó vẽ một vài phần của giao diện người dùng.
Hình 4 : Sơ đô phân cấp bố cục giao diện người dùng
Để khai báo bố cục, ta có thể khai báo đối tượng View bên trong code, tuy nhiên cách đơn giản và hiệu quả nhất là khai báo bố cục trong một file XML. Tên của một thẻ XML cho một đối tượng view tương ứng với một lớp Android thể hiện nó. Ví dụ, thẻ <TextView> tạo ra một đối tượng thuộc lớp TextView trong giao diện người dùng
2.5.3 LinearLayout
LinearLayout được dùng để bố trí các thành phần giao diện theo chiều ngang hoặc chiều dọc. LinearLayout làm cho các thành phần trong nó không bị phụ thuộc vào kích thước của màn hình. Các thành phần trong LinearLayout được dàn theo những tỷ lệ cân xứng dựa vào các ràng buộc giữa các thành phần.
LinearLayout có thuộc tính cần chú ý đó là android:orientation, thuộc tính này sẽ xác định cách bố trí của layout. Nếu khai báo android:orientation=“vertical” thì layout sẽ bố trí theo chiều dọc hoặc android:orientation=“horizontal” thì bố trí theo chiều ngang Ví dụ : ?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:paddingLeft="16dp" android:paddingRight="16dp" android:orientation="vertical" > <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="@string/to" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="@string/subject" /> <EditText android:layout_width="fill_parent" android:layout_height="0dp" android:layout_weight="1" android:gravity="top" android:hint="@string/message" /> <Button android:layout_width="100dp" android:layout_height="wrap_content" android:layout_gravity="right" android:text="@string/send" /> </LinearLayout> Kết quả :
2.5.4 FrameLayout
FrameLayout được dùng để bố trí các đối tượng theo kiểu giống như là các Layer trong Photoshop. Những đối tượng nào thuộc Layer bên dưới thì sẽ bị che khuất bởi các đối tượng thuộc Layer nằm trên. FrameLayer thường được sử dụng khi muốn tạo ra các đối tượng có khung hình bên ngoài. Có thể thêm nhiều FrameLayout chồng lên nhau và layout mới sẽ đè lên layout trước nó.
Ví dụ: <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:layout_width="251dp" android:layout_height="251dp" android:src="@drawable/ic_launcher" /> <Button android:layout_width="200px" android:layout_height="wrap_content" android:text="Print" /> </FrameLayout> Kết quả:
2.5.5 Button
Đây là một trong những widget phổ biến được sử dụng trong hầu hết ứng dụng Android. Lớp của button là android.widget.Button.
<Button android:id="@+id/button" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="28px" android:typeface="monospace" android:text="Dong y"/>
Trong một số trường hợp bắt buộc phải tùy chỉnh các widget để cho phù hợp với hoàn cảnh. Chẳng hạn như trong game, các menu hay các nút điều khiển,…lúc đó ta có thể thiết kế button bằng code.
Để khai báo một Button trong code ta làm như sau:
Button button = new Button(this); button.setText(“Nhan vao day!”); buton.setOnClickListener(…);
Nếu button đã được khai báo trong file XML ta làm như sau :
Button button = (Button)findViewById(R.layout.id.button);
Để xử lý các sự kiện khi ta nhấn vào button, ta có thể định nghĩa một phương thức trong đó bao gồm cách hành động ta muốn thực hiện. Sau đó trong file XML, ứng với thẻ chứa button ta muốn bắt sự kiện người dùng nhấn vào ta thêm vào thuộc tính android:onClick=“Ten_phuong_thuc”. Lưu ý phương thức này phải được xây dựng giống định dạng như sau, chỉ có duy nhất một tham số truyền vào là một đối tượng thuộc lớp View
public void Ten_phuong_thuc(View view) {
//Các hành động muốn thực hiện ; }
Hoặc trong file code ta có thể làm như sau:
bt_ok.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
//Các hành động muốn thực hiện khi bt_ok được nhấn
} });
2.5.6 ImageButton
Là button có hỗ trợ hiển thị hình ảnh và không hiển thị văn bản. Cách khai báo cũng tương tự button chỉ thêm thuộc tính android:src = “@drawable/ic_launcher” để thêm hình ảnh vào. <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnimage" android:src="@drawable/ic_launcher" />
Để bắt sự kiện khi người dùng nhấn vào một đối tượng ImageButton ta làm tương tự như Button được trình bày ở phần trên.
2.5.7 Dialog
Dialog là một cửa sổ nhỏ hiển thị trên màn hình nhằm nhắc người dùng đưa ra một quyết định hoặc thêm thông tin bổ sung:
AlertDialog : Dialog có thể thể hiện tiêu đề, danh sách các danh mục có thể lựa chọn, hoặc một bố cục tùy chỉnh
DatePickerDialog hoặc TimePickerDialog : Dialog với với giao diện xác định trước cho phép người dùng lựa chọn ngày hoặc giờ
Ví dụ : Tạo và hiển thị một AlertDialog
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // Add the buttons
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
//Các công việc tiếp theo khi nhấn nút OK }
});
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Cancle
} });
// Set other dialog properties ...
// Create the AlertDialog
AlertDialog dialog = builder.create(); dialog.show();
Ngoài ra ta có thể tạo một dialog tùy chỉnh với cách sau: Xây dựng bố cục của dialog trong một file XML Tạo ra một class mới
public class ShapeDialog() {
}
Khai báo một thuôc tính kiểu Context public class ShapeDialog()
{
Context context; }
Xây dựng phương thức khởi tạo cho lớp vừa tạo public class ShapeDialog()
{
Context context;
public ShapeDialog(Context c) { super();
// TODO Auto-generated constructor stub context = c;
} }
Xây dựng một phương thức để hiển thị dialog lên màn hình public void ChooseShape(final MyView mv)
{
final Dialog da = new Dialog(context); da.setContentView(R.layout.shape_dialog); da.setTitle("Choose shape");
// …
}
Cấu trúc hoàn chỉnh để tạo một dialog tùy chỉnh: public class ShapeDialog()
{
Context context;
public ShapeDialog(Context c) { super();
// TODO Auto-generated constructor stub context = c;
}
public void ChooseShape(final MyView mv) {
final Dialog da = new Dialog(context); da.setContentView(R.layout.shape_dialog); da.setTitle("Choose shape");
//Nơi khai báo, bắt các sự kiện cho các đối tượng được khai báo trong file XML
da.show(); }
}
2.5.8 Toast
Toast hiển thị phản hồi đơn giản về một hoạt động trên một cửa sổ nhỏ. Nó chỉ chiếm không gian vừa đủ cho nội dung cần hiển thị và activity hiện tại vẫn hiển thị và tương tác được. Toast sẽ tự động biến mất sau một thời gian.
Toast.makeText(getApplicationContext(), "Noi dung can hien thi ” , Toast.LENGTH_SHORT).show();
Hình 7: Minh họa Toast
Mặc định toast được hiển thị ở giữa, gần dưới cùng của màn hình. Ta có thể thay đổi vị trí này bằng cách sử dụng phương thức setGravity(int, int, int)
Ví dụ :
toast.setGravity(Gravity.TOP|Gravity.LEFT, 0, 0);
2.5.9 Gallery
Dùng để hiển thị danh sách các hình ảnh có thể cuộn được theo chiều ngang. Để sử dụng gallery ta khai báo thẻ <Gallery /> trong file XML và khai báo một đối tượng thuộc lớp Gallery trong file code liên kết đối tượng Gallery đã khai báo trong file XML
Trong file XML: <Gallery android:id="@+id/gallery" android:layout_width="match_parent" android:layout_height="300dp" android:gravity="center_vertical" android:layout_weight="1"/>
Trong file code :
Khai báo đối tượng gallery thuộc lớp Gallery: Gallery gallery;
Liên kết đến đối tượng gallery được khai báo trong file XML
Để thêm các hình ảnh vào gallery, ta dùng phương thức setAdapter().Ta cần xây dựng một lớp con của lớp BaseAdapter, lớp này có chức năng chèn các hình ảnh vào gallery. Tham số truyền vào của phương thức setAdapter(BaseAdapter base)là một đối tượng thuộc lớp này.
public class ImageAdapter extends BaseAdapter{
int galleryItemBackground;
private Context context;
public ImageAdapter(Context c){ context =c; TypedArray attr= context.obtainStyledAttributes(R.styleable.GalleryEx); galleryItemBackground= attr.getResourceId(R.styleable.GalleryEx_android_galleryItemBack ground,0); } @Override
public View getView(int position, View convertView,ViewGroup parent){
ImageView imageView;
if(convertView==null){
imageView= new ImageView(context); imageView.setLayoutParams(new
Gallery.LayoutParams((int)(display.getWidth()*0.75),(int)(displa y.getHeight()*0.75))); //imageView.setScaleType(imageView.Sc) imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); imageView.setBackgroundResource(galleryItemBackground); //imageView.setPadding(15, 15, 25, 25); } else{ imageView=(ImageView) convertView; } imageView.setImageResource(images[position]); return imageView; } }
Trước khi xây dựng lớp trên, ta cần tạo một file XML trong thư mục values của project để ta định nghĩa giao diện cho gallery.
<declare-styleable name="GalleryEx">
<attr name="android:galleryItemBackground" /> </declare-styleable>
</resources>
Để bắt sự kiện khi ta chọn hình trên gallery, ta gọi phương thức setOnItemClickListenner() của đối tượng thuộc lớp Gallery :
gallery.setOnItemClickListener(new OnItemClickListener() { @Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
// TODO Auto-generated method stub // Các công việc ta cần thục hiện }
});
2.6 LẬP TRÌNH GIAO DIỆN MỨC THẤP
Giao diện mức cao cung cấp những đối tượng được định nghĩa sẵn, giúp cho việc tạo giao diện cho ứng dụng nhanh chóng và dễ dàng. Tuy nhiên đối với một số ứng dụng việc chỉ sử dụng giao diện mức cao sẽ rất khó khăn. Chính vì vậy Android cung cấp thêm khả năng lập trình giao diện mức thấp để người lập trình có thể tự do vẽ bất cứ thứ gì mình muốn.
Trên Android, để vẽ ta cần phải có bốn thành phần cơ bản: Một đối tượng kiểu Bitmap để giữ các pixel cần vẽ.
Một đối tượng chứa nét vẽ cần vẽ ra (có thể là Rect, Path, Bitmap…). Một đối tượng kiểu Paint dùng để định nghĩa màu sắc, style… dùng để vẽ ra
màn hình.
Một đối tượng Canvas dùng để thực thi lệnh vẽ.
Để vẽ và hiển thị các đối tượng được vẽ lên màn hình ta cần xây dựng một lớp mới là một lớp mở rộng của lớp View. Trong lớp này ta cần khai báo bốn thuộc tính cơ bản cho việc vẽ đó là : bitmap, mcanvas, paint, và path lần lượt thuộc các lớp Bitmap, Canvas, Paint và Path. Đối với ứng dụng này ta sẽ tạo ra lớp MyView.
2.6.1 Bitmap
Như đã đề cập ở trên, đối tượng thuộc lớp Bitmap dùng để giữ các pixel khi ta vẽ lên. Ta cũng có thể vẽ một bitmap lên một bitmap khác thông qua đối tượng thuộc lớp Canvas bằng phương thức drawBitmap(). Có thể hình dung một cách đơn giản Bitmap như là một tờ giấy để vẽ lên như khi ta vẽ trong thực tế
Có rất nhiều phương thức để tạo một đối tượng thuộc lớp bitmap, khi tạo một đối tượng thuộc lớp Bitmap ta cần chú ý kiểu trả về của của phương thức đó. Một số phương thức trả về đối tượng bitmap có thể chỉnh sửa được, một số thì trả về bitmap không chỉnh sửa được. Đối với ứng dụng của ta, ta cần một đối tượng bitmap chỉnh sửa được vì ta cần vẽ trên bitmap đó . Vì thế ta sẽ khởi tạo đối tượng bitmap bằng phương thức sau :
bitmap = Bitmap.createBitmap(int width, int height, Bitmap.Config config);
Phương thức trên sẽ tạo ra một bitmap có thể chỉnh sửa với kích thước xác định trước thông qua 2 tham số width và height, tham số config mô tả cách các pixel được lưu trữ như thế nào. Tham số này có thể là một trong các giá trị sau: ALPHA_8, ARGB_4444, ARGB_8888, RGB_565. Trong đó ARGB_4444 cho kết quả hình ảnh thấp nên được đề nghị sử dụng ARGB_8888. ARGB_8888 lưu trữ mỗi pixel ảnh trong 4 byte. Tham số này cho chất lượng hình ảnh tốt nhất, vì thế được khuyến khích sử dụng khi có thể. Trong ứng dụng, ta sẽ sử dụng tham số này.
Phương thức createBitmap() trên có rất nhiều tham số truyền vào, ứng với các tham số khác nhau sẽ trả về các bitmap khác nhau. Ngoài tham số được truyền như trên, ta có thể truyền vào một tham số như sau:
bitmap = Bitmap.createBitmap(Bitmap src)
Với tham số được truyền như trên, phương thức createBitmap trả về một bitmap không thể chỉnh sửa từ bitmap src.
Ngoài phương thức trên, để tạo ra một đối tượng bitmap từ một bitmap sẵn có và ta muốn chỉnh sửa kích thước của bitmap đó ta có thể dùng phương thức sau :
bitmap = Bitmap.createScaledBitmap(Bitmap src, int width, int height, boolean filter);
Phương thức trên trả về một bitmap có kích thước định bởi tham số width và height và giống với bitmap src.
Các phương thức trên thuộc lớp Bitmap của gói andoid.graphics, gói này còn có lớp BitmapFactory. Trong lớp này sẽ cung cấp các phương thức để giải mã các hình ảnh từ các nguồn bên ngoài và lưu trữ vào đối tượng thuộc lớp Bitmap, trong ứng dụng ta sẽ sử dụng 2 phương thức sau:
BitmapFactory.decodeResource (Resources res, int id): trả về một bitmap được giải mã từ đối tượng res của lớp
Resource(Resource là lớp dùng để truy cập các tài nguyên của ứng dụng), res ở đây là đối tượng chứa đựng dữ liệu hình ảnh tương ứng với tham số id .
BitmapFactory.decodeStream(InputStream is, null, null): giải mã một luồng nhập vào một bitmap
2.6.2 Canvas
Các đối tượng thuộc lớp Canvas có nhiệm vụ thực thi các phương thức vẽ. Trong lớp MyView ta cần gọi lại hàm onDraw(Canvas canvas). Phương thức này có nhiệm vụ vẽ các đối tượng lên màn hình. Trong phương thức này ta có một tham số canvas là một đối tượng thuộc lớp Canvas. Ta cần phân biệt đối tượng này với đối tượng mcanvas ta đã khai báo trước đó. Đây là hai đối tượng hoàn toàn khác nhau.
Khi hàm onDraw(Canvas canvas) được gọi, thì chỉ có những đối tượng được vẽ thông qua tham số canvas mới hiện lên màn hình. Khi đó ta có thể hình dung hiện tại trong ứng dụng của ta sẽ có hai màn hình, một màn hình ở trên là nơi để tham số canvas vẽ lên, màn hình bên dưới là bitmap ta đã khai báo trước đó. Các đối tượng được vẽ lên bitmap sẽ thông qua đối tượng mcanvas. Trong phương thức khởi tạo ta sẽ khai báo mcanvas = new Canvas(bitmap) , khi đó các đối tượng được vẽ thông qua mcanvas sẽ được vẽ lên bitmap.Khi mcanvas vẽ lên bitmap thì chúng vẫn chưa được hiển thị lên màn hình. Để hiển thị bitmap ta đã khai báo lên màn hình thì trong hàm onDraw(Canvas canvas) ta thực hiện phương thức drawBitmap()
canvas.drawBitmap(bitmap,0,0,null);
Đối tượng thuộc lớp Canvas có rất nhiều phương thức vẽ như : drawRect, drawCirle, drawLine(), drawBitmap, drawPath()… Ta sẽ tìm hiểu một số phương thức thông dụng:
drawRect(float left, float top, float right,float bottom,Paint paint) : phương thức này dùng để vẽ một hình chữ nhật xác định bởi điểm trên bên trái có tọa độ (left,top) và điểm dưới bên phải có tọa độ (right, bottom) với màu sắc, độ lớn nét vẽ … được định nghĩa trong tham số paint
drawCircle(float cx, float cy,float radius, Paint paint): phương thức này dùng để vẽ một hình tròn có tâm tại tọa độ (cx,cy) bán kính radius với màu sắc, độ lớn nét vẽ … được định nghĩa trong tham số paint. drawLine(float startX, float startY,float stopX,float
đường thẳng bắt đầu từ điểm có tọa độ (startX,startY) đến điểm có tọa độ (stopX,stopY)
drawBitmap(Bitmap bitmap, float left, float top, Paint paint) :dùng để vẽ một bitmap tại tọa độ (left, top) sử dụng các giá trị của tham số paint
Với các phương thức trên drawRect(), drawCircle(), drawLine() … ta chỉ vẽ được những hình đơn giản, để vẽ những hình phức tạp hơn như : tam giác, ngôi sao, đa giác .. ta phải sử dụng phương thức drawPath(Path path, Paint paint) , phương thức này cho phép vẽ ra đối tượng path, sử dụng các tham số được định nghĩa trong paint. Đối tượng path sẽ được định nghĩa trong những phần tiếp theo.
2.6.3 Paint
Các đối tượng thuộc lớp Paint có nhiệm vụ xác định màu, độ lớn nét vẽ và các hiệu ứng cho nét vẽ …
paint.setColor(int color ) :dùng để thiết lập màu cho nét vẽ
paint.setStrokeWidth(float width) :dùng để thiết lập độ lớn nét vẽ paint.setStyle(Paint.Style style) : thiết lập chế độ
vẽ(Style.STROKE), tô(Style.FILL), hay vừa tô vừa vẽ(Style.FILL_AND_STROKE).
paint.setAntiAlias(boolean a): dùng để thiết lập/bỏ thiết lập khử răng cưa, làm cho nét vẽ mượt hơn
paint.setPathEffect(PathEffect effect ):thiết lập hiệu ứng cho nét vẽ
paint.setShadowLayer(float radius, float dx, float dy, int color): tạo bóng bên dưới nét vẽ.
paint.setMaskFilter(MaskFilter mask): thiết lập hoặc hủy thiết lập đối tượng MaskFilter
2.6.4 Path
Như đề cập ở trên, để vẽ các hình dạng phức tạp ta dùng phương thức drawPath(Path path, Paint paint). Các đối tượng thuộc lớp Path sẽ chứa đựng các đường phức tạp, các đường này có thể bao gồm đường thẳng, đường cong, hoặc các đường khép kín. Với việc kết hợp trên ta có thể tạo ra những hình phức tạp tùy ý. Ví dụ: để tạo ra hình ngôi sao, ta sẽ kết hợp vẽ nhiều đoạn thẳng lại với nhau, hoặc hình nửa vầng