36 bài
Đầu tiên các bạn cần clone dự án về máy tính của mình và sử dụng android studio để viết code, ngôn ngữ lập trình là java
Để xây dựng môt view chúng ta cần tạo các phần sau
1.Fragment_items_list.xml
Tên file cần tạo: fragment_item_list.xml
Cấu trúc tên
Fragment_items_list.xml: trong đó items là các đối tượng mà chúng ta muốn hiển thị ví dụ như Fragment_cars_list.xml, Fragment_pets_list.xml...
Nơi đặt file này: app/src/main/res/layout
Nơi này để chứa danh sách các items
Nội dung của file như sau
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="loadingMore"
type="boolean" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/itemListRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingTop="@dimen/space_16"
android:paddingStart="@dimen/space_20"
android:paddingEnd="@dimen/space_20"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="@+id/progressBar3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<ProgressBar
android:id="@+id/progressBar3"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:indeterminate="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:visibleGone="@{loadingMore}" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Trong đó các bạn lưu ý phần bôi đậm.
itemListRecyclerView: Phần này là tên của RecyclerView. ở đây items là một đối tượng mà chúng ta muốn tạo, ví dụ carsRecyclerView. petsRecyclerView....
Nội dung file
Tên file : item.java: trong đó item là đối tượng muốn tạo, ví dụ car.java, pet.java (Chú ý tên đối tượng cố gắng đặt tên bằng tiếng anh và không phải là số nhiều) và các bạn phải đổi tên file của mình đi nhé. tôi lấy ví dụ item để cho nó chung chung thôi
Nơi đặt File: app/src/main/java/com/vantinviet/adayroionline/viewobject
package com.vantinviet.adayroionline.viewobject;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import com.google.gson.annotations.SerializedName;
@Entity(primaryKeys = "id")// đối với mỗi đối tượng sẽ phải có khóa chính thường thì id là khóa chính
public class Item {
@NonNull
@SerializedName("id")
public String id="";
@SerializedName("property1") // lấy key theo api trả về
public String property1=""; // thuộc tính 1 của đối tượng
@SerializedName("property2") // lấy key theo api trả về
public String property2=""; // thuộc tính 2 của đối tượng
@SerializedName("added_date")
public final String addedDate;
//thêm nhiều thuộc tính nữa ở đây, nhớ thêm cả ở phương thức khởi tạo nữa nhé
//Phương thức khởi tạo
public Item() {
}
public Item(@NonNull String id, String property1, String property2, String added_date) {
this.id = id!=null?id:this.id;
this.property1=property1!=null?property1:this.property1;
this.property2= property2!=null?property2:this.property2;
this.added_date=added_date!=null?added_date:this.added_date;
}
}
Nếu như trong quá trình đọc tài liệu sẽ có phần bổ xung thêm thuộc tích thì các bạn nhớ vào đây thêm thuộc tính nhé
Kiến thức cơ bản cho phần này: các bạn phải nắm được cách định nghĩa class của lập trình hướng đối tượng
Nhưng nếu trước mắt các bạn chưa nắm được thì chỉ cần hiểu đại khái thế này, các đối tượng thì sẽ có các thuộc tính của nó là property1,property2...., nếu có nhiều hơn thuộc tính thì thêm vào
còn nữa, các thuộc tính này có các kiểu dữ liệu khác nhau do đó cũng cần phải nắm được các kiểu dữ liệu nguyên thủy trong lập trình nói chung và lập trình java nói riêng
Nội dung file, giả sử item của chúng tả chỉ hiển thị mỗi name và có trường là item_name
Nơi đặt file fragment_item.xml: res/layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item" <! -- Đặt tên item để lát hiển thị item_name ở dưới -->
type="com.vantinviet.adayroionline.viewobject.Item" /> <! -- Gọi đối tượng item trong viewobject -->
</data>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<! -- Tạo một ô text view để hiện thị item name -->
<TextView
android:id="@+id/item_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:text="@{item.item_name}" <! -- Gọi giá trị của item_name -->
android:textAlignment="viewStart"
android:textColor="@color/md_black_1000"
android:textSize="@dimen/font_title_size"
app:font='@{"normal"}'
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"
tools:text="Name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</layout>
Chú ý quan trọng: việc xây dựng hiển thị mội item là rất quan trọng do đó các bạn cần phải nắm được các đối tượng thể hiện của item
ví dụ như hiển thị ảnh của item, hiển thị ngày tháng, và hiển thị rất nhiều các thông tin khác.
Để tìm hiểu sâu về phần này các bạn tham khảo tại đây
Xây dựng lớp DAO
Ứng dụng sử dụng cơ dữ liệu sqlite để lưu dữ liệu được lấy từ phía server về, điều này giúp cho người dùng có cảm giá dữ liệu đang online, giúp tăng phần trải nghiệm người dùng
Thêm file có tên là itemDao.java vào thư mục java/com/vantinviet/adayroionline/db
Nội dung file
package com.vantinviet.adayroionline.db;
import com.vantinviet.adayroionline.viewobject.Item;
import java.util.List;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
@Dao
public interface ItemDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List<Item> items);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Item item);
@Query("SELECT * FROM Item WHERE id = :id")
LiveData<Item> getItemById(String id);
@Query("SELECT * FROM Item ORDER BY addedDate desc")
LiveData<List<Item>> getAllItems();
@Query("DELETE FROM Item")
void deleteAll();
}
Các bạn nhớ thay bằng file nội dung của mình nhé
Để có thêm thao tác được với itemDao thì chúng ta cần phải thêm vào PSCoreDb
Mở file PSCoreDb theo đường dẫn java/com/vantinviet/adayroionline/db/PSCoreDb.java
import viewobject item vào trước

Bổ xung item vào như hình

Chú ý mũi tên đỏ: mỗi làm khi them một dao bạn cần phải tăng giá trị này lên 1, để hệ thống android thêm được db vào sqlite
Tiếp theo cần khai báo dòng sau như hình

Bổ xung dao vào appModule, mở file java/com/vantinviet/adayroionline/di/AppModule.java
import class ItemDao

Thêm dòng sau vào cuối class
@Singleton
@Provides
ItemDao provideItemDao(PSCoreDb db) {
return db.itemDao();
}
Bước 4-1: Xây dựng API gọi Item theo các điều kiện
Mở file java/com/vantinviet/adayroionline/api/PSApiService.java
//import view object của Item
import com.vantinviet.adayroionline.viewobject.Item;
Bổ xung xuống cuối của class này
@GET("api/items/get/api_key/{API_KEY}/limit/{limit}/offset/{offset}")
LiveData<ApiResponse<List<Item>>> getAllItem(@Path("API_KEY") String api_key, @Path("limit") String limit, @Path("offset") String offset);
@GET("api/items/get/api_key/{API_KEY}/id/{id}")
LiveData<ApiResponse<Item>> getItemById(@Path("API_KEY") String api_key, @Path("id") String id);
Bạn nhấn vào đây là làm theo nhé.
Nơi tạo file java/com/vantinviet/adayroionline/ApiResponse
Tên file cần tạo: ApiResponseGetItemList
Nội dung file
package com.vantinviet.adayroionline.ApiResponse;
import com.google.gson.annotations.SerializedName;
import com.vantinviet.adayroionline.viewobject.Item;
import java.util.List;
public class ApiResponseGetItemList {
@SerializedName("result")
public String result;
@SerializedName("code")
public String code;
@SerializedName("errorMessage")
public String errorMessage;
@SerializedName("list_order")
public List<Item> itemList;
public ApiResponseGetItemList(String result, String code, String errorMessage, List<Item> itemList) {
this.result = result;
this.code = code;
this.errorMessage = errorMessage;
this.itemList = itemList;
}
@Override
public String toString() {
return "ApiResponseGetItemList{" +
"result='" + result + '\'' +
", code='" + code + '\'' +
", errorMessage='" + errorMessage + '\'' +
", itemList=" + itemList +
'}';
}
}
Tên file ApiResponseGetItem.java
Nội dung file
package com.vantinviet.adayroionline.ApiResponse;
import com.google.gson.annotations.SerializedName;
import com.vantinviet.adayroionline.viewobject.Item;
public class ApiResponseGetItem {
@SerializedName("result")
public String result;
@SerializedName("code")
public String code;
@SerializedName("errorMessage")
public String errorMessage;
@SerializedName("data")
public Item item;
public ApiResponseGetItem(String result, String code, String errorMessage, Item item) {
this.result = result;
this.code = code;
this.errorMessage = errorMessage;
this.item = item;
}
@Override
public String toString() {
return "ApiResponseGetItem{" +
"result='" + result + '\'' +
", code='" + code + '\'' +
", errorMessage='" + errorMessage + '\'' +
", item=" + item +
'}';
}
}
Nơi tạo java/com/vantinviet/adayroionline/repository
Tạo một thư mục có tên item trong java/com/vantinviet/adayroionline/repository
Tạo file file ItemsListRepository bên trong java/com/vantinviet/adayroionline/repository/item
Nội dung file
package com.vantinviet.adayroionline.repository.item;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.vantinviet.adayroionline.ApiResponse.ApiResponseGetItemList;
import com.vantinviet.adayroionline.AppExecutors;
import com.vantinviet.adayroionline.api.ApiResponse;
import com.vantinviet.adayroionline.api.PSApiService;
import com.vantinviet.adayroionline.db.ItemDao;
import com.vantinviet.adayroionline.db.PSCoreDb;
import com.vantinviet.adayroionline.repository.common.PSRepository;
import com.vantinviet.adayroionline.utils.Utils;
import com.vantinviet.adayroionline.viewobject.Item;
import com.vantinviet.adayroionline.viewobject.common.Resource;
import java.io.IOException;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import retrofit2.Response;
@Singleton
public class ItemListRepository extends PSRepository {
//region variable
private final ItemDao itemDao;
//end region
//region constructor
@Inject
ItemListRepository(PSApiService psApiService, AppExecutors appExecutors, PSCoreDb db, ItemDao itemDao) {
super(psApiService, appExecutors, db);
this.itemDao = itemDao;
}
//region User Repository Functions for ViewModel
public LiveData<Resource<List<Item>>> getAllItem(String api_key,String limit, String offset) {
final MutableLiveData<Resource<List<Item>>> statusLiveData = new MutableLiveData<>(); // To update the status to the listener
appExecutors.networkIO().execute(() -> {
try {
// Call the API Service
Response<ApiResponseGetItemList> response =
psApiService.getAllItem(api_key,limit,offset).execute();
// Wrap with APIResponse Class
ApiResponse<ApiResponseGetItemList> apiResponseGetItemList = new ApiResponse<>(response);
if(apiResponseGetItemList.body.code.equals("101")){
statusLiveData.postValue(Resource.error(apiResponseGetItemList.body.errorMessage, null));
return;
}
// If response is successful
if (apiResponseGetItemList.isSuccessful()) {
db.beginTransaction();
try {
itemDao.deleteAll();
db.setTransactionSuccessful();
} catch (Exception e) {
Utils.psErrorLog("Error in doing transaction of recent transaction list.", e);
} finally {
db.endTransaction();
}
statusLiveData.postValue(Resource.success(apiResponseGetItemList.body.itemList));
} else {
statusLiveData.postValue(Resource.error(apiResponseGetItemList.errorMessage, null));
}
} catch (IOException e) {
statusLiveData.postValue(Resource.error(e.getMessage(), null));
}
});
return statusLiveData;
}
// Get next page transaction list
public LiveData<Resource<Boolean>> getNextPageItemList(String api_key,String limit,String offset) {
final MutableLiveData<Resource<Boolean>> statusLiveData = new MutableLiveData<>(); // To update the status to the listener
appExecutors.networkIO().execute(() -> {
try {
// Call the API Service
Response<ApiResponseGetItemList> response =
psApiService.getAllItem(api_key,limit,offset).execute();
// Wrap with APIResponse Class
ApiResponse<ApiResponseGetItemList> apiResponseGetItemList = new ApiResponse<>(response);
if(apiResponseGetItemList.body.code.equals("101")){
statusLiveData.postValue(Resource.error(apiResponseGetItemList.body.errorMessage, null));
return;
}
// If response is successful
if (apiResponseGetItemList.isSuccessful()) {
db.beginTransaction();
try {
itemDao.deleteAll();
itemDao.insertAll(apiResponseGetItemList.body.itemList);
db.setTransactionSuccessful();
} catch (Exception e) {
Utils.psErrorLog("Error in doing transaction of recent item list.", e);
} finally {
db.endTransaction();
}
} else {
statusLiveData.postValue(Resource.error(apiResponseGetItemList.errorMessage, null));
}
} catch (IOException e) {
statusLiveData.postValue(Resource.error(e.getMessage(), null));
}
});
return statusLiveData;
}
public LiveData<Item> getItemById(String item_id) {
return itemDao.getItemById(item_id);
}
}
Một view mode dùng để kết gọi đến api lấy dữ liệu trả về và thực hiện các theo tác trả dữ liệu cho một adapter
Tên file : ItemViewModel
Nơi lưu: java/com/vantinviet/adayroionline/viewmodel/item
Chú ý là bạn cần tạo một thư mục item nhé
Nội dung file
package com.vantinviet.adayroionline.viewmodel.item;
import com.vantinviet.adayroionline.Config;
import com.vantinviet.adayroionline.repository.item.ItemRepository;
import com.vantinviet.adayroionline.utils.AbsentLiveData;
import com.vantinviet.adayroionline.viewmodel.common.PSViewModel;
import com.vantinviet.adayroionline.viewobject.Item;
import com.vantinviet.adayroionline.viewobject.common.Resource;
import java.util.List;
import javax.inject.Inject;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations;
public class ItemViewModel extends PSViewModel {
private final LiveData<Resource<List<Item>>> itemsData;
private MutableLiveData<TmpDataHolder> itemsObj = new MutableLiveData<>();
private final LiveData<Resource<Boolean>> nextPageItemsData;
private MutableLiveData<TmpDataHolder> nextPageItemsObj = new MutableLiveData<>();
private final LiveData<Resource<Item>> itemByIdData;
private MutableLiveData<ItemByIdTmpDataHolder> itemByIdObj = new MutableLiveData<>();
public String shopName, shopId;
@Inject
ItemViewModel(ItemRepository repository) {
itemsData = Transformations.switchMap(itemsObj, obj -> {
if (obj == null) {
return AbsentLiveData.create();
}
return repository.getItemList(obj.limit, obj.offset);
});
nextPageItemsData = Transformations.switchMap(nextPageItemsObj, obj -> {
if (obj == null) {
return AbsentLiveData.create();
}
return repository.getNextPageItemList(Config.API_KEY, obj.limit, obj.offset);
});
itemByIdData = Transformations.switchMap(itemByIdObj, obj -> {
if (obj == null) {
return AbsentLiveData.create();
}
return repository.getItemById(obj.id);
});
}
public void setItemsObj(String limit, String offset) {
TmpDataHolder tmpDataHolder = new TmpDataHolder(limit, offset);
this.itemsObj.setValue(tmpDataHolder);
}
public LiveData<Resource<List<Item>>> getItemsData() {
return itemsData;
}
public void setNextPageItemsObj(String limit, String offset) {
TmpDataHolder tmpDataHolder = new TmpDataHolder(limit, offset);
this.nextPageItemsObj.setValue(tmpDataHolder);
}
public LiveData<Resource<Boolean>> getNextPageItemsData() {
return nextPageItemsData;
}
public void setItemByIdObj(String id) {
ItemByIdTmpDataHolder itemByIdTmpDataHolder = new ItemByIdTmpDataHolder(id);
this.itemByIdObj.setValue(itemByIdTmpDataHolder);
}
public LiveData<Resource<Item>> getItemByIdData() {
return itemByIdData;
}
class TmpDataHolder {
String limit, offset;
public TmpDataHolder(String limit, String offset) {
this.limit = limit;
this.offset = offset;
}
}
class ItemByIdTmpDataHolder {
String id;
private ItemByIdTmpDataHolder(String id) {
this.id = id;
}
}
}
Chú ý Item ở trong nội dung file này là một đối tượng cụ thể mà chúng ta cần theo tác với đối tượng đó.
Việc xây dựng hàm, class các bạn cần phải nắm được trong lập trình hướng đối tượng
Ngoài ra còn có một số những nội dung khác ở class này nhưng trước mắt các bạn chưa cần phải nắm hết, chỉ cần nắm được các nội dung nêu ở trên
Mở file java/com/vantinviet/adayroionline/di/ViewModelModule.java
import itemViewModel vào như hình

Chú bạn phải đổi item thành cái của các bạn nhé
Tiếp theo khai báo phương thức trừu tượng như hình

Các bạn nhớ đổi tên item thành cái của các bạn nhé
Tạo file activity_item_list.xml có nội dung như sau, nhớ đặt tên file theo đúng đối tượng mà bạn định làm nhé
Nơi lưu: res/layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_content"
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
style="@style/ToolBarStyle.Event"
android:background="@color/global__primary"
android:id="@+id/toolbar"
android:layout_height="?android:attr/actionBarSize"
android:layout_width="match_parent"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
Nội dung của file gần như bạn không phải chỉnh sửa gì
Tên file item_item_list_adapter.xml
Nơi lưu: res/layout
Nội dung file
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.vantinviet.adayroionline.viewobject.Item" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/md_white_1000">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="@color/md_white_1000">
<TextView
android:id="@+id/itemNameTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:text="@{item.item_name}"
android:textAlignment="viewStart"
android:textColor="@color/md_black_1000"
android:textSize="@dimen/font_title_size"
app:font='@{"normal"}'
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"
tools:text="Name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Phần này là phần dây dựng các tính năng thao tác với 1 item
Đầu tiên chúng ta cần phải xây dựng một adapter : hiểu nôm na là adapter là một cầu nối dữ liệu giữa data và hiển thị lên app
Chúng ta cần phải tạo một thư mục có tên là item trong thư mục app/src/main/java/com/vantinviet/adayroionline/ui
khi đã có thư mục item rồi chúng ta cần tạo một thư mục trong item có tên là adapter.
Tiếp theo chúng ta sẽ cần phải tạo file java có tên là ItemListAdapter.java (Nhắc lại mội lần nữa về item) ví dụ CarListAdapter.java, PetListAdapter.java: Chú là các chữ cái phải viết hóa nhé như Car. Pet
Nơi đặt file ItemListAdapter.java app/src/main/java/com/vantinviet/adayroionline/ui/item/adapter
Nội dung file:
package com.vantinviet.adayroionline.ui.item.adapter;
/*
Đưa vào một số thư viện để code, trong quá trình code các bạn chú ý không phải copy nguyên và cũng sẽ có những thứ các bạn cần và không cần
*/
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.databinding.DataBindingUtil;
import com.squareup.picasso.Picasso;
import com.vantinviet.adayroionline.Lib.Factory;
import com.vantinviet.adayroionline.R;
/*
Các bạn lưu ý ở đây các bạn nhìn thấy có hai cái item nhưng các bạn chỉ đối tên cái item bôi đậm ví dụ ItemItemBinding, ItemPetBinding
*/
import com.vantinviet.adayroionline.databinding.ItemItemListAdapterBinding; // Item này là một đối tượng layout lát nữa chúng ta sẽ tạo đối tượng layout này. nếu nó đang lỗi các bạn cứ để nguyên đó không cần phải lo lắng
import com.vantinviet.adayroionline.ui.common.DataBoundListAdapter;
import com.vantinviet.adayroionline.ui.common.DataBoundViewHolder;
import com.vantinviet.adayroionline.utils.CircleTransform;
import com.vantinviet.adayroionline.utils.Constants;
import com.vantinviet.adayroionline.utils.Objects;
import com.vantinviet.adayroionline.utils.Utils;
import com.vantinviet.adayroionline.viewobject.Item; //Đối tượng view object này chúng ta đã tạo ra ở bước 2 rồi, giờ cũng cần phải đưa vào
public class ItemListAdapter extends DataBoundListAdapter<Item /* Truyền đối tượng item vào */, ItemItemListAdapterBinding /*Nhớ đọc kỹ ở phần trên để thay thế cho đúng*/> {
private final androidx.databinding.DataBindingComponent dataBindingComponent;
private final ItemClickCallback callback; // khai báo một sự kiện click (nhấn vào danh sách)
private DiffUtilDispatchedInterface diffUtilDispatchedInterface = null;
public ItemListAdapter(androidx.databinding.DataBindingComponent dataBindingComponent,
ItemClickCallback callback/*tham số callback để khi người dùng nhấn vào*/) {
this.dataBindingComponent = dataBindingComponent;
this.callback = callback; //Tạo một đối tượng click callback
}
//Một số phương thức ghi đè cần có
@Override
protected ItemItemListAdapterBinding createBinding(ViewGroup parent) {
ItemItemListAdapterBinding binding = DataBindingUtil
.inflate(LayoutInflater.from(parent.getContext()),
R.layout.item_item_list_adapter/* item_item_list_adapter đã được tạo ở bước 5.2 */, parent, false,
dataBindingComponent);
binding.getRoot().setOnClickListener(v -> {
});
return binding;
}
// For general animation
@Override
public void bindView(DataBoundViewHolder<ItemItemListAdapterBinding> holder, int position) {
super.bindView(holder, position);
}
@Override
protected void dispatched() {
if (diffUtilDispatchedInterface != null) {
diffUtilDispatchedInterface.onDispatched();
}
}
@Override
protected void bind(ItemItemListAdapterBinding binding, Item item) {
//Bây giờ chúng ta sẽ bắt đầu hiển thị dữ liệu cho một item. ví dụ trong trường ví dụ trong trường hợp này chúng ta chỉ cần hiển thị item_name. như vậy các bạn sẽ phải có thuộc tính item_name, thì bạn cần phải bổ sung cho view object ở bước 2 nhé
//Utils.psLog("item.item_name",item.item_name);
//set giá trị cho text
binding.itemNameTextView.setText(item.item_name);
Utils.psLog("some log","content log");
}
/*
Một số phương thức này để kiểm tra khi cần update dữ liệu hiển thị
*/
@Override
protected boolean areItemsTheSame(Item oldItem, Item newItem) {
return Objects.equals(oldItem.id, newItem.id)
&& oldItem.item_name.equals(newItem.item_name);
}
@Override
protected boolean areContentsTheSame(Item oldItem, Item newItem) {
return Objects.equals(oldItem.id, newItem.id)
&& oldItem.item_name.equals(newItem.item_name);
}
public interface ItemClickCallback {
void onClick(Item item);
}
}
Chú ý: các item là một đối tượng cụ thể mà chúng ta muốn sử lý ví dụ car, pet
Tên file cần tạo ItemListFragment.java
Nơi đặt file : java/com/vantinviet/adayroionline/ui/item
Nội dung file
//Tên gói
package com.vantinviet.adayroionline.ui.item;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.vantinviet.adayroionline.Config;
import com.vantinviet.adayroionline.R;
import com.vantinviet.adayroionline.binding.FragmentDataBindingComponent;
//import Fragment_Item_List
import com.vantinviet.adayroionline.databinding.FragmentItemListBinding;
//import ItemListAdapter
import com.vantinviet.adayroionline.ui.item.adapter.ItemListAdapter;
import com.vantinviet.adayroionline.ui.common.DataBoundListAdapter;
import com.vantinviet.adayroionline.ui.common.PSFragment;
import com.vantinviet.adayroionline.utils.AutoClearedValue;
import com.vantinviet.adayroionline.utils.Utils;
import com.vantinviet.adayroionline.viewmodel.item.ItemViewModel;
import com.vantinviet.adayroionline.viewobject.Item;
import com.vantinviet.adayroionline.viewobject.common.Status;
import java.util.List;
public class ItemListFragment extends PSFragment implements DataBoundListAdapter.DiffUtilDispatchedInterface {
private final androidx.databinding.DataBindingComponent dataBindingComponent = new FragmentDataBindingComponent(this);
private ItemViewModel itemViewModel;
@VisibleForTesting
private AutoClearedValue<FragmentItemListBinding> binding;
private AutoClearedValue<ItemListAdapter> adapter;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//set item fragment sử dụng layout fragment_item_list
FragmentItemListBinding dataBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_items_list, container, false, dataBindingComponent);
binding = new AutoClearedValue<>(this, dataBinding);
return binding.get().getRoot();
}
//Khởi tạo các ui
@Override
protected void initUIAndActions() {
//kiểm tra khi có sự kiện scroll của itemListRecyclerView
binding.get().itemListRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
//Khi người dùng kéo xong
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
LinearLayoutManager layoutManager = (LinearLayoutManager)
recyclerView.getLayoutManager();
if (layoutManager != null) {
int lastPosition = layoutManager
.findLastVisibleItemPosition();
if (lastPosition == adapter.get().getItemCount() - 1) {
if (!binding.get().getLoadingMore() && !itemViewModel.forceEndLoading) {
itemViewModel.loadingDirection = Utils.LoadingDirection.bottom;
int limit = Config.LIST_ITEMS_COUNT;
itemViewModel.start = itemViewModel.start + limit;
itemViewModel.setLoadingState(true);
itemViewModel.setNextPageItemsObj(String.valueOf(Config.LIST_ITEMS_COUNT), String.valueOf(itemViewModel.start));
}
}
}
}
});
binding.get().swipeRefresh.setColorSchemeColors(getResources().getColor(R.color.view__primary_line));
binding.get().swipeRefresh.setProgressBackgroundColorSchemeColor(getResources().getColor(R.color.global__primary));
binding.get().swipeRefresh.setOnRefreshListener(() -> {
itemViewModel.loadingDirection = Utils.LoadingDirection.top;
// reset itemViewModel.offset
itemViewModel.start = 0;
// reset itemViewModel.forceEndLoading
itemViewModel.forceEndLoading = false;
itemViewModel.setItemsObj(String.valueOf(Config.LIST_ITEMS_COUNT), String.valueOf(itemViewModel.start));
// update live data
});
}
@Override
protected void initViewModels() {
itemViewModel = ViewModelProviders.of(this, viewModelFactory).get(ItemViewModel.class);
}
@Override
protected void initAdapters() {
ItemListAdapter nvAdapter = new ItemListAdapter(dataBindingComponent, item -> navigationController.navigateToItemActivity/*Hiện tại hàm này chưa được xây dựng, chúng ta sẽ làm ở bước này nhấn vào đây để xem [Chú ý là bước này chúng ta sẽ làm sau nhé] */(ItemListFragment.this.getActivity(), item.id), this);
this.adapter = new AutoClearedValue<>(this, nvAdapter);
binding.get().itemListRecyclerView.setAdapter(adapter.get());
}
//Khởi tạo dữ liệu
@Override
protected void initData() {
// bắt đầu lấy dữ liệu
itemViewModel.setItemsObj(String.valueOf(Config.LIST_ITEMS_COUNT), String.valueOf(itemViewModel.start));
//Quan sát khi có dữ liệu được lấy về
itemViewModel.getItemsData().observe(this, result -> {
if (result != null) {
switch (result.status) {
//Nếu lấy dữ liệu về thành công
case SUCCESS:
replaceItemList(result.data);
itemViewModel.setLoadingState(false);
break;
//nếu vẫn đang trong quá trình tải
case LOADING:
replaceItemList(result.data);
break;
//nếu phát sinh một lỗi bất kỳ
case ERROR:
itemViewModel.setLoadingState(false);
break;
}
}
});
//Quan sát khi người dùng kéo scroll xuống dưới
itemViewModel.getNextPageItemsData().observe(this, state -> {
if (state != null) {
if (state.status == Status.ERROR) {
itemViewModel.setLoadingState(false);
itemViewModel.forceEndLoading = true;
}
}
});
itemViewModel.getLoadingState().observe(this, loadingState -> {
binding.get().setLoadingMore(itemViewModel.isLoading);
if (loadingState != null && !loadingState) {
binding.get().swipeRefresh.setRefreshing(false);
}
});
}
//Thực hiện việc cập nhật lại dữ liệu item
private void replaceItemList(List<Item> items) {
this.adapter.get().replace(items);
binding.get().executePendingBindings();
}
@Override
public void onDispatched() {
if (itemViewModel.loadingDirection == Utils.LoadingDirection.top) {
if (binding.get().itemListRecyclerView != null) {
LinearLayoutManager layoutManager = (LinearLayoutManager)
binding.get().itemListRecyclerView.getLayoutManager();
if (layoutManager != null) {
layoutManager.scrollToPosition(0);
}
}
}
}
}
Mở file java/com/vantinviet/adayroionline/di/MainActivityModule.java
Sau đó nhấn vào dấu cộng để mở rộng nội dung
Thêm nội dung ItemListFragment vào MainActivityModule
nội dung như sau
import com.vantinviet.adayroionline.ui.item.ItemListFragment;
Kéo xuống cuối cùng thêm dòng như hình
Nhớ là thay item bằng cái của bạn nhé. trong trường hợp này của tôi là item nên tôi thêm như vậy
Tên File : ItemListActivity.java
Nơi lưu file : java/com/vantinviet/adayroionline/ui/item
Nội dung file
package com.vantinviet.adayroionline.ui.item;
import android.os.Bundle;
import com.vantinviet.adayroionline.R;
import com.vantinviet.adayroionline.databinding.ActivityItemListBinding;
import com.vantinviet.adayroionline.ui.common.PSAppCompactActivity;
import androidx.databinding.DataBindingUtil;
public class ItemListActivity extends PSAppCompactActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityItemListBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_item_list);
initUI(binding);
}
private void initUI(ActivityItemListBinding binding) {
// Toolbar
initToolbar(binding.toolbar, getResources().getString(R.string.item_list_title/*Bạn cần bổ xung ngôn ngữ vào string Xem ở đây*/));
// setup Fragment
setupFragment(new ItemListFragment());
}
}
Mở file AndroidManifest.xml theo đường dẫn sau
Tìm đến dòng cuối của thẻ application
Bổ xung itemListActivity vào
Chú ý là nếu đã có itemListActivity rồi thì bạn không được thêm nữa mà chỉ thêm có một lần thôi
Mở file res/menu/menu_drawer.xml

Thao tác như tác như sau

Các bạn nhớ thay item của các bạn bằng thứ các bạn muốn nhé, ví dụ như car, pet
Hiện tại trang detail chưa xây dựng, do đó chúng ta chỉ xây dựng hàm trống để những bước sau chúng ta điều chỉnh lại
Mở file sau: java/com/vantinviet/adayroionline/ui/common/NavigationController.java
Bổ xung phương thức sau vào cuối class
public void navigateToItemActivity(Activity activity,String itemId) {
Intent intent = new Intent(activity, ItemActivity.class);
//Truyền dữ liệu tới ItemActivity
intent.putExtra("itemId",itemId);
activity.startActivity(intent);
}
Sau khi chúng ta xây dựng xong phần list item. giờ chúng ta đi đến phần xây dựng chi tiết item
Tạo file fragment_item_detail.xml trong res/layout
Nội dung file
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.vantinviet.adayroionline.viewobject.Item" />
</data>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/itemNameTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:text="@{item.item_name}"
android:textAlignment="viewStart"
android:textColor="@color/md_black_1000"
android:textSize="@dimen/font_title_size"
app:font='@{"normal"}'
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"
tools:text="Name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</layout>
Các bạn nhớ thay item thành cái mình làm nhé
Một Activity là gì : các bạn hiểu nôm na là nơi chứ các fragment để hiển thị dữ liệu
Đầu tiên chúng ta cần tạo Activity trước có têm là ItemActivity. java
Nơi lưu là app/src/main/java/com/vantinviet/adayroionline/ui/item
Nội dung của file
//Khai báo tên gói package
package com.vantinviet.adayroionline.ui.item;
import android.os.Bundle;
import com.vantinviet.adayroionline.R;
import com.vantinviet.adayroionline.databinding.ActivityItemBinding;// lát chúng ta sẽ cần phải tạo một layout có tên ActivityItem hiện giờ các bạn sẽ thấy bị lỗi nên đừng lo lắng quá
import com.vantinviet.adayroionline.ui.common.PSAppCompactActivity;
import androidx.databinding.DataBindingUtil;
public class ItemActivity extends PSAppCompactActivity {
//Phương thức tạo layout
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Khi tạo layout này các bạn sẽ thấy xuất hiện lỗi tạm thời cứ để đó đã lát chúng ta sẽ thêm ActivityItem
ActivityItemBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_item /*lát sẽ thêm activity_item*/);
initUI(binding);
}
// Đoạn này cũng sẽ phát sinh lỗi
private void initUI(ActivityItemBinding binding) {
// Toolbar
initToolbar(binding.toolbar, getResources().getString(R.string.item_title /*Chúng ta sẽ cần tạo một string cho title của layout này lát đến phần thêm ngôn ngữ chúng ta sẽ tạo chúng*/));
// setup Fragment tại bước tiếp theo chúng ta sẽ tạo ra một ItemFragment nên cũng sẽ bị lỗi các bạn cũng không cần phải lo lắng
setupFragment(new ItemFragment());
}
}
Như vậy chúng ta đã tạo xong activity. các bạn chú ý các item nhé, ví dụ như car, pet
Nội dung file
package com.vantinviet.adayroionline.ui.item;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.ViewModelProviders;
import com.vantinviet.adayroionline.R;
import com.vantinviet.adayroionline.binding.FragmentDataBindingComponent;
import com.vantinviet.adayroionline.databinding.FragmentItemBinding;
import com.vantinviet.adayroionline.ui.common.PSFragment;
import com.vantinviet.adayroionline.utils.AutoClearedValue;
import com.vantinviet.adayroionline.utils.Constants;
import com.vantinviet.adayroionline.utils.PSDialogMsg;
import com.vantinviet.adayroionline.viewmodel.item.ItemViewModel;
public class ItemFragment extends PSFragment {
private final androidx.databinding.DataBindingComponent dataBindingComponent = new FragmentDataBindingComponent(this);
private ItemViewModel itemViewModel;
private String itemId;
private PSDialogMsg psDialogMsg;
@VisibleForTesting
private AutoClearedValue<FragmentItemDetailBinding> binding;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
FragmentItemDetailBinding dataBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_item_detail, container, false, dataBindingComponent);
binding = new AutoClearedValue<>(this, dataBinding);
return binding.get().getRoot();
}
@Override
protected void initUIAndActions() {
psDialogMsg = new PSDialogMsg(getActivity(), false);
}
@Override
protected void initViewModels() {
itemViewModel = ViewModelProviders.of(this, viewModelFactory).get(ItemViewModel.class);
}
@Override
protected void initAdapters() {
}
@Override
protected void initData() {
if (getActivity() != null) {
itemId = getActivity().getIntent().getStringExtra(Constants.ITEM_ID);
}
if (itemId!= null && !itemId.isEmpty()) {
itemViewModel.setItemByIdObj(blogId);
itemViewModel.getItemByIdData().observe(this, result -> {
if (result != null) {
if (result.data != null) {
switch (result.status) {
case SUCCESS:
binding.get().setItem(result.data);
break;
case ERROR:
psDialogMsg.showErrorDialog(getString(R.string.item_detail__error_message), getString(R.string.app__ok));
psDialogMsg.show();
break;
case LOADING:
binding.get().setItem(result.data);
break;
}
}
}
});
}
}
}
File activity_item là file hiển thị nội dung của một item
Nơi đặt file res/layout
Nội dụng file activity_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_content"
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
style="@style/ToolBarStyle.Event"
android:background="@color/global__primary"
android:id="@+id/toolbar"
android:layout_height="?android:attr/actionBarSize"
android:layout_width="match_parent"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/md_white_1000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
Hiện tại app đang xây dựng trên 2 ngôn ngữ chính đó là tiếp anh và tiếng việt
Các file string lưu theo cấu trúc sau

Giả sử chúng có cần xây dựng 3 view cần các string sau
1.itemList danh sách
Chúng ta cần title của list
2.Item Xem chi tiết một item
3.ItemForm sửa một item
cấu trúc lưu
<string name="item_title">title</string>
Nếu AndroidManifest.xml của ứng dụng không được thiết lập chính xác thì bạn có thể gặp phải rất nhiều sự cố – có thể hệ thống Android sẽ không thể định vị tất cả Activity và Services của bạn. Tại sao lại như thế?
Trong bài viết này, freetuts sẽ giới thiệu và phân tích cho các bạn về file AndroidManifest trong Android, giúp các bạn hiểu hơn về bản chất của file AndroidManifest để xử lí các sự cố nếu có gặp phải do bắt nguồn từ file này.
AndroidManifest.xml là file quan trọng nhất trong một ứng dụng Android, nó cung cấp thông tin về ứng dụng cho hệ thống Android, bao gồm tên ứng dụng, biểu tượng ứng dụng, phiên bản ứng dụng, quyền truy cập và các thành phần khác của ứng dụng.
Trong file AndroidManifest.xml, các phần quan trọng được định nghĩa bởi các thẻ.
Bài viết này được đăng tại [free tuts .net]
Ví dụ: Thẻ <manifest> chứa thông tin chung về ứng dụng như tên package, phiên bản tối thiểu của hệ điều hành Android được yêu cầu, tên ứng dụng, thẻ <activity> định nghĩa các activity...
Nếu bạn tạo một project Android bằng Android Studio, thì một file Manifest duy nhất sẽ tự động được tạo cho bạn, sau đó điền tất cả các yếu tố cần thiết để project này chạy trên thiết bị Android.

Đoạn code dưới đây là file Manifest được tự động tạo bởi "Empty Activity" của project trong Android Studio.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<?xml version="1.0" encoding="utf-8"?> package="com.jessicathornsby.myapplication"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest> |
Hầu hết các mục Manifest bao gồm một phần tử và một thuộc tính.Nếu bạn cần chỉ định nhiều thuộc tính cho cùng một phần tử, thì thông thường bạn sẽ lặp lại phần tử đó với các thuộc tính khác thay vì thêm nhiều thuộc tính vào cùng một phần tử. Ví dụ ở đây, chúng tôi đang khai báo nhiều thuộc tính cho phần tử <uses-feature>:
|
1
2
3
4
5
6
7
8
|
<uses-feature android:name="android.hardware.sensor.proximity" android:required="true" /><uses-feature android:name="android.hardware.sensor.relative_humidity" android:required="true" /><uses-feature android:name="android.hardware.sensor.heartrate" android:required="true" /> |
Bây giờ chúng ta sẽ phân tích kỹ hơn về bản chất của file AndroidManifest.xml trong Android.
Ngoài những thành phần chính của file AndroidManifest.xml thì còn một số thuộc tính quan trọng mà lập trình viên cần lưu ý:
android:versionCode: Định nghĩa version code của ứng dụng. Version code là một số nguyên, được sử dụng để phân biệt các version khác nhau của ứng dụng.android:versionName: Định nghĩa version name của ứng dụng. Version name là một chuỗi ký tự, được sử dụng để giới thiệu về version của ứng dụng cho người dùng.android:label: Định nghĩa tên của ứng dụng hiển thị trên giao diện người dùng.android:icon: Định nghĩa biểu tượng của ứng dụng hiển thị trên giao diện người dùng.android:theme: Định nghĩa giao diện của một hoạt động hoặc của toàn bộ ứng dụng.android:exported: Xác định xem thành phần có thể được gọi từ ứng dụng khác hay không.android:enabled: Xác định xem thành phần có hoạt động hay không.android:configChanges: Xác định các sự kiện thay đổi cấu hình mà hoạt động có thể xử lý mà không cần bị khởi động lại.android:screenOrientation: Xác định hướng màn hình của activity.android:permission: Xác định quyền truy cập cần thiết để sử dụng thành phần.Nắm vững các thành phần và thuộc tính này sẽ giúp bạn hiểu rõ hơn về cách ứng dụng Android hoạt động và cách thiết kế, xây dựng các thành phần trong nó.
Mỗi project Android Studio phải chứa ít nhất một file Manifest , nhưng một project cũng có thể chứa nhiều file Manifest. Điều này có thể xảy ra trong các trường hợp sau:
Để ghép nhiều file AndroidManifest.xml lại với nhau, bạn có thể sử dụng các công cụ build của Android Studio, bao gồm cả Gradle và Android Asset Packaging Tool (AAPT).
Tùy thuộc vào nhu cầu và công cụ build mà bạn sử dụng, bạn có thể tìm hiểu thêm về cách thực hiện việc ghép nhiều file AndroidManifest.xml lại với nhau trong tài liệu hướng dẫn của Gradle và Android Asset Packaging Tool.
Dưới đây là các bước ghép và kiểm tra việc ghép file AndroidManifest.xml.
Dạng xem file Manifest đã hợp nhất hiển thị kết quả hợp nhất ở bên trái và thông tin về file Manifest đã hợp nhất ở bên phải.

Nếu bạn bối rối về bất kỳ thành phần nào của file Manifest được hợp nhất, thì bạn có thể xem thêm thông tin về một thành phần cụ thể bằng cách chọn thành phần đó trong ngăn bên trái, sau đó đọc “Manifest log ” trong ngăn bên phải.

Nếu có bất kỳ xung đột hợp nhất nào xảy ra thì chúng sẽ xuất hiện trong phần “Merging Errors” ở phía bên tay phải của Android Studio, kèm theo một số đề xuất về cách giải quyết xung đột cụ thể này, bằng cách sử dụng các điểm đánh dấu quy tắc hợp nhất.
Trong bài viết này, chúng ta đã tìm hiểu về thành phần cấu trúc của file AndroidManifest.xml. Việc hiểu rõ các thành phần này sẽ giúp bạn tối ưu hóa quản lý và phát triển ứng dụng Android của mình. Ngoài ra, chúng ta cũng đã tìm hiểu cách để ghép nhiều file AndroidManifest.xml lại với nhau, qua việc sử dụng các công cụ build của Android Studio như Gradle và Android Asset Packaging Tool (AAPT). Hy vọng những kiến thức mà freetuts cung cấp về AndroidManifest.xml trong Android phần nào giúp bạn hiểu được bản chất của nó, hỗ trợ bạn phát triển ứng dụng của mình.
Các thành phần được viết bằng Java và được liệt kê trong các thư mục mô-đun trong thư mục java trong dạng xem Dự án: Android. Mỗi tên mô-đun bắt đầu bằng tên miền (chẳng hạn như com.example.android ) và bao gồm tên ứng dụng.
Ví dụ sau đây cho thấy một thành phần hoạt động :
MainActivity).
Ở trên cùng của tệp MainActivity.java là một packagecâu lệnh xác định gói ứng dụng. Tiếp theo là một importkhối cô đặc trong hình trên, với " ...". Nhấp vào dấu chấm để mở rộng khối để xem nó. Các importcâu lệnh nhập thư viện cần thiết cho ứng dụng, chẳng hạn như câu lệnh sau, sẽ nhập thư viện AppCompatActivity:
import android.support.v7.app.AppCompatActivity;
Mỗi hoạt động trong một ứng dụng được triển khai dưới dạng một lớp Java. Khai báo lớp sau mở rộng AppCompatActivitylớp để triển khai các tính năng theo cách tương thích ngược với các phiên bản Android trước:
public class MainActivity extends AppCompatActivity {
...
}
Như bạn đã tìm hiểu trước đó, trước khi hệ thống Android có thể khởi động một thành phần ứng dụng chẳng hạn như một hoạt động, hệ thống phải biết rằng hoạt động đó tồn tại bằng cách đọc tệp AndroidManifest.xml của ứng dụng. Do đó, mỗi hoạt động phải được liệt kê trong tệp AndroidManifest.xml.
Tài nguyên bố cục được viết bằng XML và được liệt kê trong thư mục bố cục trong thư mục res trong dạng xem Project: Android. Nhấp vào res > layout rồi nhấp đúp vào activity_main.xml để xem tệp bố cục trong ngăn chỉnh sửa.
Android Studio hiển thị dạng xem Thiết kế của bố cục, như thể hiện trong hình bên dưới. Chế độ xem này cung cấp ngăn Bảng màu gồm các thành phần giao diện người dùng và lưới hiển thị bố cục màn hình.
Gói ứng dụng Android (APK) là định dạng tệp gói để phân phối và cài đặt ứng dụng di động Android. Quá trình xây dựng bao gồm các công cụ và quy trình tự động chuyển đổi từng dự án thành APK.
Android Studio sử dụng Gradle làm nền tảng của hệ thống xây dựng, với nhiều khả năng dành riêng cho Android hơn được cung cấp bởi Plugin Android dành cho Gradle. Hệ thống xây dựng này chạy như một công cụ tích hợp từ menu Android Studio.
Khi bạn tạo một dự án, Android Studio sẽ tự động tạo các tệp bản dựng cần thiết trong thư mục Gradle Scripts trong Project: Android view. Các tệp bản dựng Android Studio được đặt tên build.gradle như hình bên dưới:
Mỗi dự án có những điều sau đây:
Đây là tệp xây dựng cấp cao nhất cho toàn bộ dự án, nằm trong thư mục gốc của dự án, xác định các cấu hình xây dựng áp dụng cho tất cả các mô-đun trong dự án của bạn. Không được chỉnh sửa tệp này do Android Studio tạo để bao gồm các thành phần phụ thuộc của ứng dụng.
Android Studio tạo build.gradle (Module: app)các tệp riêng biệt cho từng mô-đun. Bạn có thể chỉnh sửa cài đặt bản dựng để cung cấp các tùy chọn đóng gói tùy chỉnh cho từng mô-đun, chẳng hạn như loại bản dựng bổ sung và hương vị sản phẩm, đồng thời ghi đè cài đặt trong tệp kê khai hoặc tệp build.gradle cấp cao nhất. Tệp này thường là tệp cần chỉnh sửa nhất khi thay đổi cấu hình cấp ứng dụng, chẳng hạn như khai báo các thành phần phụ thuộc trong phần dependencies. Phần sau đây hiển thị nội dung của build.gradle (Module: app)tệp dự án:
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "com.example.android.helloworld2"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.1'
testCompile 'junit:junit:4.12'
}
Các tệp build.gradle sử dụng cú pháp Gradle. Gradle là Ngôn ngữ dành riêng cho miền (DSL) để mô tả và thao tác logic xây dựng bằng Groovy , đây là ngôn ngữ động cho Máy ảo Java (JVM). Bạn không cần học Groovy để thực hiện các thay đổi, vì Plugin Android dành cho Gradle giới thiệu hầu hết các thành phần DSL mà bạn cần.
Mẹo : Để tìm hiểu thêm về plugin Android DSL, hãy đọc tài liệu tham khảo DSL .
Trong tệp trên build.gradle (Module: app), câu lệnh đầu tiên áp dụng các tác vụ xây dựng trình cắm Gradle dành riêng cho Android:
apply plugin: 'com.android.application'
android {
...
}
Khối android { }chỉ định những điều sau đây cho bản dựng:
compileSdkVersion 24
buildToolsVersion "24.0.1"
Các mục nhập và cài đặt cốt lõi cho ứng dụng được chỉ định trong một defaultConfig { }khối trongandroid { } block:
...
defaultConfig {
applicationId "com.example.hello.helloworld"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
}
...
Cài đặt minSdkVersionvà targetSdkVersionghi đè mọi cài đặt AndroidManifest.xml cho phiên bản SDK tối thiểu và phiên bản SDK mục tiêu. Xem phần "Khai báo phiên bản Android" trước đó trong chương này để biết thông tin cơ bản về các cài đặt này.
Tuyên testInstrumentationRunnerbố bổ sung hỗ trợ thiết bị để thử nghiệm giao diện người dùng với Espresso và UIAutomator. Chúng được mô tả trong một bài học riêng biệt.
Các loại bản dựng cho ứng dụng được chỉ định trong một buildTypes { }khối, khối này kiểm soát cách ứng dụng được tạo và đóng gói.
...
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
...
Loại bản dựng được chỉ định là releasedành cho bản phát hành của ứng dụng. Một loại bản dựng phổ biến khác là debug. Việc định cấu hình các loại bản dựng được mô tả trong một bài học riêng.
Các phần phụ thuộc cho ứng dụng được xác định trong dependencies { }khối, đây là một phần của tệp build.gradle có nhiều khả năng thay đổi nhất khi bạn bắt đầu phát triển mã phụ thuộc vào các thư viện khác. Khối này là một phần của API Gradle tiêu chuẩn và thuộc bên ngoài khối android { }.
...
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.0'
testCompile 'junit:junit:4.12'
}
Trong đoạn mã trên, câu lệnh compile fileTree(dir: 'libs', include: ['*.jar'])thêm phần phụ thuộc của tất cả các tệp ".jar" bên trong libsthư mục. Cấu compilehình biên dịch ứng dụng chính — mọi thứ trong đó được thêm vào đường dẫn lớp biên dịch và cũng được đóng gói vào APK cuối cùng.
Khi bạn thực hiện các thay đổi đối với tệp cấu hình bản dựng trong một dự án, Android Studio yêu cầu bạn đồng bộ hóa các tệp dự án để có thể nhập các thay đổi cấu hình bản dựng và chạy một số kiểm tra để đảm bảo cấu hình sẽ không tạo ra lỗi bản dựng.
Để đồng bộ hóa các tệp dự án, hãy nhấp vào Đồng bộ hóa ngay trong thanh thông báo xuất hiện khi thực hiện thay đổi hoặc nhấp vào Đồng bộ hóa dự án từ thanh menu. Nếu Android Studio nhận thấy bất kỳ lỗi nào với cấu hình — ví dụ: nếu mã nguồn sử dụng các tính năng API chỉ khả dụng ở cấp API cao hơn compileSdkVersion— cửa sổ Thông báo sẽ xuất hiện để mô tả sự cố.
Với trình giả lập thiết bị ảo, bạn có thể thử nghiệm một ứng dụng trên các thiết bị khác nhau như máy tính bảng hoặc điện thoại thông minh — với các cấp API khác nhau cho các phiên bản Android khác nhau — để đảm bảo ứng dụng đó hoạt động tốt và phù hợp với hầu hết người dùng. Mặc dù đó là một ý tưởng hay nhưng bạn không cần phải phụ thuộc vào việc có sẵn thiết bị vật lý để phát triển ứng dụng.
Trình quản lý Thiết bị ảo Android (AVD) tạo một thiết bị ảo hoặc trình giả lập mô phỏng cấu hình cho một loại thiết bị Android cụ thể. Sử dụng Trình quản lý AVD để xác định các đặc điểm phần cứng của thiết bị và cấp độ API của thiết bị, đồng thời lưu thiết bị đó dưới dạng cấu hình thiết bị ảo. Khi bạn khởi động trình giả lập Android, nó sẽ đọc một cấu hình đã chỉ định và tạo một thiết bị giả lập trên máy tính của bạn hoạt động giống hệt như phiên bản vật lý của thiết bị đó.
Để chạy trình giả lập trên máy tính của bạn, hãy sử dụng Trình quản lý AVD để tạo cấu hình mô tả thiết bị ảo. Chọn Công cụ > Android > Trình quản lý AVD hoặc nhấp vào biểu tượng Trình quản lý AVD
trên thanh công cụ.
Màn hình "Thiết bị ảo của bạn" xuất hiện hiển thị tất cả các thiết bị ảo đã tạo trước đó. Nhấp vào nút +Tạo thiết bị ảo để tạo một thiết bị ảo mới.
Bạn có thể chọn một thiết bị từ danh sách các thiết bị phần cứng được xác định trước. Đối với mỗi thiết bị, bảng hiển thị kích thước màn hình chéo (Kích thước), độ phân giải màn hình tính bằng pixel (Độ phân giải) và mật độ điểm ảnh (Mật độ). Ví dụ: mật độ pixel của thiết bị Nexus 5 là xxhdpi, có nghĩa là ứng dụng sử dụng các biểu tượng trong thư mục xxhdpi của thư mục mipmap . Tương tự như vậy, ứng dụng cũng sẽ sử dụng bố cục và đối tượng có thể vẽ từ các thư mục được xác định cho mật độ đó.
Bạn cũng chọn phiên bản hệ thống Android cho thiết bị. Tab Đề xuất hiển thị các hệ thống được đề xuất cho thiết bị. Các phiên bản khác có sẵn trong tab Hình ảnh x86 và Hình ảnh khác .
Để chạy ứng dụng trên thiết bị ảo mà bạn đã tạo trong phần trước, hãy làm theo các bước sau:
trên thanh công cụ.Trình giả lập khởi động và khởi động giống như một thiết bị vật lý. Tùy thuộc vào tốc độ máy tính của bạn, quá trình này có thể mất một lúc. Ứng dụng được xây dựng và khi trình giả lập đã sẵn sàng, Android Studio sẽ tải ứng dụng lên trình giả lập và chạy nó.
Bạn sẽ thấy ứng dụng được tạo từ mẫu Hoạt động trống ("Xin chào thế giới") như minh họa trong hình dưới đây, cũng hiển thị ngăn Chạy của Android Studio hiển thị các tác vụ được thực hiện để chạy ứng dụng trên trình giả lập.

Trong hình trên:
Luôn kiểm tra ứng dụng của bạn trên thiết bị thực vì người dùng sẽ sử dụng ứng dụng trên thiết bị thực. Mặc dù trình giả lập khá tốt nhưng chúng không thể hiển thị tất cả các trạng thái có thể có của thiết bị, chẳng hạn như điều gì sẽ xảy ra nếu có cuộc gọi đến trong khi ứng dụng đang chạy. Để chạy ứng dụng trên thiết bị vật lý, bạn cần những điều sau:
Để Android Studio giao tiếp với thiết bị, hãy bật Gỡ lỗi USB trên thiết bị Android. Trên phiên bản Android 4.2 trở lên, màn hình Tùy chọn nhà phát triển bị ẩn theo mặc định. Làm theo các bước sau để bật USB Debugging:
Nhấn bảy lần vào thông tin Số bản dựng .
Bạn đã đọc chính xác: Hãy chạm vào nó bảy lần .
Bây giờ, hãy kết nối thiết bị và chạy ứng dụng từ Android Studio.
Nếu Android Studio không nhận ra thiết bị, hãy thử cách sau:
Nếu máy tính của bạn vẫn không tìm thấy thiết bị hoặc thông báo là "trái phép":
Ngắt kết nối thiết bị khỏi máy tính của bạn.
Trên thiết bị, chọn Cài đặt > Tùy chọn nhà phát triển .
Nhấn Thu hồi ủy quyền Gỡ lỗi USB .
Kết nối lại thiết bị với máy tính của bạn.
Khi được nhắc, hãy cấp quyền.
Nhật ký là một công cụ sửa lỗi mạnh mẽ mà bạn có thể sử dụng để xem xét các giá trị, đường dẫn thực thi và ngoại lệ. Sau khi bạn thêm câu lệnh ghi nhật ký vào ứng dụng, thông báo nhật ký của bạn sẽ xuất hiện cùng với thông báo nhật ký chung trong tab logcat của ngăn Màn hình Android của Android Studio.
Để xem ngăn Màn hình Android, hãy nhấp vào nút Android Monito r ở cuối cửa sổ chính của Android Studio. Màn hình Android cung cấp hai tab:
Báo cáo ghi nhật ký thêm bất kỳ thông báo nào bạn chỉ định vào nhật ký. Việc thêm các câu lệnh ghi nhật ký tại các điểm nhất định trong mã cho phép nhà phát triển xem xét các giá trị, đường dẫn thực thi và ngoại lệ.
Ví dụ: câu lệnh ghi nhật ký sau đây thêm "MainActivity"và "Hello World"vào nhật ký:
Log.d("MainActivity", "Hello World");
Sau đây là các yếu tố của tuyên bố này:
Log: Lớp Nhật ký là API để gửi thông điệp tường trình.d: Bạn chỉ định một mức nhật ký để bạn có thể lọc các thông báo tường trình bằng menu thả xuống ở giữa ngăn tab logcat . Sau đây là các mức nhật ký bạn có thể chỉ định:
d: Chọn Debug hoặc Verbose để xem các thông báo này.e: Chọn Error hoặc Verbose để xem các thông báo này.w: Chọn Cảnh báo hoặc Chi tiết để xem các thông báo này.i: Chọn Thông tin hoặc Chi tiết để xem các thông báo này."MainActivity": Đối số đầu tiên là thẻ nhật ký có thể được sử dụng để lọc thư trong tab logcat . Đây thường là tên của hoạt động mà thông báo bắt nguồn từ đó. Tuy nhiên, bạn có thể biến điều này thành bất kỳ thứ gì hữu ích cho mình để gỡ lỗi ứng dụng. Cách thực hành tốt nhất là sử dụng hằng số làm thẻ nhật ký, như sau: private static final String LOG_TAG =
MainActivity.class.getSimpleName();
Log.d(LOG_TAG, "Hello World");
"Hello World": Đối số thứ hai là thông báo thực tế xuất hiện sau cấp nhật ký và thẻ nhật ký trong tab logcat .Ngăn Chạy xuất hiện thay cho ngăn Màn hình Android khi bạn chạy ứng dụng trên trình giả lập hoặc thiết bị. Sau khi bắt đầu chạy ứng dụng, hãy nhấp vào nút Màn hình Android ở cuối cửa sổ chính, sau đó nhấp vào tab logcat trong ngăn Màn hình Android nếu nó chưa được chọn.
Trong hình trên:
onCreate()phương thức của MainActivity.logcatthông báo nhật ký, bao gồm thông báo từ câu lệnh ghi nhật ký.Theo mặc định, màn hình nhật ký được đặt thành Verbose trong menu thả xuống ở đầu màn hình logcat để hiển thị tất cả các thông báo. Bạn có thể thay đổi điều này thành Gỡ lỗi để xem các thư bắt đầu bằng Log.dhoặc thay đổi thành Lỗi để xem các thư bắt đầu bằng Log.e, v.v.
Tên file cần tạo : fragment_item.xml
Nơi tạo file res/layout
Nội dung file
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="car"
type="com.vantinviet.adayroionline.viewobject.Car" />
</data>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/car_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:text="@{car.car_name}"
android:textAlignment="viewStart"
android:textColor="@color/md_black_1000"
android:textSize="@dimen/font_title_size"
app:font='@{"normal"}'
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"
tools:text="Name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</layout>
Ghi chú
Tài liệu được cập nhật thường xuyên. Mọi góp ý xin gửi về đội ngũ Softway.