移動編程—安卓開發總結
- 專案結構
- UI界面
- LinearLayout
- RelativeLayout
- 事件處理
- SQLite資料庫
- 動態資料渲染
- 頁面傳值
- 遠程資料庫連接
- JDBC連接
- HTTP連接
- 異步UI重繪
專案結構
-
AndroidManifest.xml:注冊activity (新建時IDE自動注冊) -
java后臺代碼
MainActivity.java: 程式入口, 同級目錄檔案夾下存放其他activity- activity: 控制每個頁面
- adaptor:控制向前端資料渲染
- view:控制UI界面元素
- bean:存放資料庫中對應物體類
- utils:封裝工具類,如用于判斷從前端獲取輸入是否為空等
-
layout
- 包含activity ,控制界面元素及其格式
-
drawable
- 包含shape,背景形狀控制元件
-
mipmap
- 包含圖片
-
values
string.xml需要使用的字串值,便于統一修改color.xml需要使用的顏色RGB值,便于直接使用dimension.xml需要用到的尺寸,便于尺寸適配不同機型
-
gradle
-
build.gradle 配置相關依賴
-
gradle-wrapper.properties 配置gradle版本等資訊
-
UI界面
參考博客:安卓UI界面開發經典四大布局,此處只記錄(B站教學視頻里)最常用的兩種布局方式,
LinearLayout
整個Android布局中的控制元件擺放方式是以線性的方式擺放的,最常用,
排列方式:
- 縱向:
android:orientation="vertical" - 橫向:
android:orientation="horizontal"
系統默認采用橫向布局
權重:
類似H5前端開發彈性布局中flex的數值,設定總權重時部分權重超過后的元素將被忽略,
線性布局中可以規定控制元件的權重,通過android:layout_weight=""實作,下面我們來看一起權重的經典問題,我們先不設定總權重,設定子元素的寬度為0dp,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:gravity="center_vertical" //對齊方式
android:orientation="horizontal" //布局方向
>
<!--橫向布局-->
<ImageView
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:layout_weight="1"/>
<ImageView
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="@color/colorAccent"
android:layout_weight="2"/>
</LinearLayout>
顯示效果如下:
RelativeLayout
通過相對布局,可以實作控制元件的重疊,當控制元件大量重疊時,用相對布局更加方便,
它在MarginLayout的基礎上,添加了對齊方法layout_alignBottom="@+id/iv",
下面是一些簡單的屬性:
| 屬性 | 作用 |
|---|---|
| layout_marginRight | 控制元件與界面右側距離 |
| layout_toRightOf | 將該控制元件的右邊緣與給定ID的控制元件左邊緣對齊 |
| layout_alignRight | 將該控制元件的右邊緣與給定ID的右邊緣對齊 |
| layout_alignParentRight | 將該控制元件的右部與其父控制元件的右部對齊 |
| layout_centerInParent | 將該控制元件的置于父控制元件的中央 |
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="horizontal"
android:weightSum="3">
<View
android:id="@+id/v1"
android:layout_width="300dp"
android:layout_height="200dp"
android:background="@color/colorPrimaryDark" />
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@color/colorAccent"
android:layout_alignRight="@id/v1"
android:layout_alignBottom="@id/v1"
android:layout_marginRight="50dp"/>
</RelativeLayout>
效果圖如下:
事件處理
方法1:通過ID系結設定事件監聽器實作:
在前端指定元素ID:
<Button
android:id="@+id/btn_login"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="@string/register"
android:background="@drawable/login_btn">
</Button>
后臺根據ID找到元素:
public class MainActivity extends AppCompatActivity {
private Button btnLogin; //宣告變數
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//根據ID找到元素
btnLogin = findViewById(R.id.btn_login);
//系結點擊事件
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/*事件處理*/
}
});
}
}
方法2:實作前端onClick點擊事件
前端:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="Login"/>
后臺:
public void Login(View view) {
/*處理事件*/
}
SQLite資料庫
參考教程:B站安卓零基礎入門-第85講
以單例模式創建工具類,需要注意SQLiteDatabase中主鍵必須是自增_id
public class DBHelper extends SQLiteOpenHelper {
private static SQLiteOpenHelper mInstance;
public static synchronized SQLiteOpenHelper getInstance(Context context){
if (mInstance == null){
//創建資料庫
mInstance = new DBHelper(context, "droid.db", null, 1);
}
return mInstance;
}
private DBHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
//創建資料庫sql陳述句
String sql = "CREATE TABLE book(" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
"name TEXT," +
"type TEXT," +
"course TEXT" +
")";
//執行sql陳述句
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
操作資料庫資料:
需要注意execSQL函式的第二個引數順序需要和待填寫項對應
public class BookDB{
private static BookDB bookDB;
private SQLiteOpenHelper dbHelper;
public static synchronized BookDB getInstance(Context context){
if(bookDB == null){
bookDB = new BookDB(context);
}
return bookDB;
}
private BookDB(Context context){
dbHelper = DBHelper.getInstance(context);
}
//修改資料庫
public void insert(Book book){
SQLiteDatabase db = dbHelper.getWritableDatabase();
if(db.isOpen()){
db.execSQL("INSERT INTO book (name, type, course) VALUES (? , ?, ?)",
new Object[]{book.getName(), book.getId(), book.getCourse()});
}
db.close();
}
//查詢資料庫
public List<Book> listBooks(){
SQLiteDatabase db = dbHelper.getReadableDatabase();
List<Book> list = new ArrayList<>();
if(db.isOpen()){
Cursor cursor = db.rawQuery("select * from book", null);
while(cursor.moveToNext()){
Book book = new Book();
book.setId(cursor.getInt(cursor.getColumnIndex("_id")));
book.setName(cursor.getString(cursor.getColumnIndex("name")));
book.setType(cursor.getString(cursor.getColumnIndex("type")));
book.setCourse(cursor.getString(cursor.getColumnIndex("course")));
list.add(book);
}
cursor.close();
}
db.close();
return list;
}
}
動態資料渲染
參考教程:B站訂單APP專案實戰-第6集
完成動態資料渲染需要以下準備:
adapter.javabean.javaactivity.javaactivity_list.xmlitem.xml
下面以顯示從本地資料庫查詢到的多本書籍資訊為例子,效果圖如下:
1、撰寫book_item.xml完成單項布局
2、撰寫activity_book_list.xml,創建ListView視圖顯示資料項
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp">
<com.example.myapplication.view.BookListView
android:id="@+id/book_list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</ScrollView>
此處為了解決ListView的高度適配問題,新建了視圖類BookListView重寫了onMeasure方法:
public class BookListView extends ListView {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE<<2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightSpec);
}
}
3、撰寫book.java存放資料庫對應物體類
4、撰寫BookAdaptor.java,控制資料向book_item.xml布局中渲染
public class BookAdapter extends BaseAdapter {
private Context context; //語境
private List<Book> data; //資料集
public BookAdapter(Context context){
data = new LinkedList<>();
this.context = context;
}
//getter and setter of data
public List<Book> getBookList() {
return data;
}
public void setBookList(List<Book> bookList) {
this.data.clear();
this.data.addAll(bookList);
notifyDataSetChanged();
}
//重寫父類的各個方法
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int position) {
return data.get(position);
}
@Override
public long getItemId(int position) {
return data.get(position).getId();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if(convertView == null){
//加載資料單項layout: book_item.xml
convertView = LayoutInflater.from(context).inflate(R.layout.book_item, null);
//根據系結界面元素
viewHolder = new ViewHolder();
viewHolder.bookName = convertView.findViewById(R.id.book_name);
viewHolder.bookType = convertView.findViewById(R.id.book_type);
viewHolder.course = convertView.findViewById(R.id.course);
viewHolder.editBtn = convertView.findViewById(R.id.edit_btn);
viewHolder.deleteBtn = convertView.findViewById(R.id.delete_btn);
convertView.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder) convertView.getTag();
}
//向前端渲染資料
Book book = data.get(position);
viewHolder.bookName.setText(book.getName());
viewHolder.bookType.setText("型別:"+book.getType());
viewHolder.course.setText("課程:"+book.getCourse());
//系結點擊事件,實作頁面傳值(代碼見相應小節)
return convertView;
}
//ViewHolder類,關聯前端界面元素
class ViewHolder{
TextView bookName, bookType, course;
Button editBtn, deleteBtn;
}
}
5、在BookListActivity.java中,系結Adaptor與前端ListView
public class BookListActivity extends BookDB {
private ListView listView;
private BookAdapter adapter;
private BookDB bookDB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_list);
bookDB = bookDB.getInstance(this);
//初始化頁面
initView();
}
private void initView() {
//獲取資料
List<Book> list = bookDB.listBooks();
adapter = new BookAdapter(this);
adapter.setBookList(list);
//給前端 ListView 系結 Adaptor
listView = findViewById(R.id.book_list);
listView.setAdapter(adapter);
}
}
頁面傳值
以上小節中點擊書籍單項后跳轉到詳情為例,跳轉時需要攜帶點擊的書籍ID,用于在詳情頁查詢相關資訊,
跳轉要求:攜帶物體類Book.java作為引數,需要該類實作介面implements Serializable
1、傳遞引數
在BookAdaptor.java的getView()方法回傳前系結點擊跳轉事件,轉遞點擊物件作為引數
//系結點擊事件
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, BookDetailActivity.class);
intent.putExtra("book", book); //傳值
context.startActivity(intent); //跳轉
}
});
2、接收引數
書籍詳情頁面BookEditActivity.java接收引數,并初始化視圖
public class BookEditActivity extends BaseActivity {
private Book book;
private EditText nameText;
private EditText typeText;
private EditText courseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_edit);
//得到頁面傳遞的引數
book = (Book) getIntent().getSerializableExtra("book");
System.out.println(book.getName());
//系結元素
nameText = findViewById(R.id.book_name);
typeText = findViewById(R.id.book_type);
courseText = findViewById(R.id.course);
//初始化頁面
nameText.setText(book.getName());
typeText.setText(book.getType());
courseText.setText(book.getCourse());
}
public void editBook(View view) {
book = editBookInfo();
BookDB bookDB = BookDB.getInstance(this);
bookDB.update(book);
showToast("修改書籍資訊成功");
this.finish();
}
private Book editBookInfo() {
String name = nameText.getText().toString().trim();
String type = typeText.getText().toString().trim();
String course = courseText.getText().toString().trim();
if(StringUtils.isValid(name) && StringUtils.isValid(type) && StringUtils.isValid(course)){
book.setName(name);
book.setType(type);
book.setCourse(course);
}
else {
return null;
}
return book;
}
}
遠程資料庫連接
注意事項:
1、安卓客戶端中網路請求均為異步,不可以在主執行緒中進行,
2、需要安裝合適版本驅動包并在build.gradle中配置
3、需要在AndroidManifest.xml中允許網路請求
參考教程:CSDNAndroid連接Mysql資料庫教程
JDBC連接
public class JDBCHelper{
private final String DRIVER = "com.mysql.jdbc.Driver";
private final String URL = "jdbc:mysql://www.ylxteach.net:3366/ydbc2021";
private final String USER = "******";
private final String PWD = "******";
//工具類,采用單例模式
private static JDBCHelper instance;
private JDBCHelper(){}
public static JDBCHelper getInstance() {
if(instance == null){
instance = new JDBCHelper();
}
return instance;
}
//修改資料庫
public void addBook(Book book){
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USER, PWD);
PreparedStatement statement = conn.prepareStatement(
"INSERT INTO lmj_book (bname, type, course) VALUES (? , ?, ?)");
statement.setString(1, book.getName());
statement.setString(2, book.getType());
statement.setString(3, book.getCourse());
statement.executeUpdate();
statement.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
});
thread.start();
}
}
HTTP連接
1、在gradle中匯入依賴
okhttp3用于發送客戶端請求,gson用于決議服務器回傳訊息,
implementation 'com.google.code.gson:gson:2.6.2'
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'
2、決議服務器回傳訊息
可以看到資料庫資訊在aaData陣列中:
通過定義工具類實作圖書資訊決議:
需要注意的是Book中每個成員變數的命名需要與資料庫中欄位名稱對應,否則決議失敗,
public class BookJsonParser {
private static BookJsonParser instance;
private BookJsonParser(){
}
public static BookJsonParser getInstance(){
if(instance==null){
return new BookJsonParser();
}
else {
return instance;
}
}
public List<Book> getBook(String content){
//獲取回傳結果的aaData陣列
JsonObject returnObj = new JsonParser().parse(content).getAsJsonObject();
JsonArray data = returnObj.get("aaData").getAsJsonArray();
//決議為書籍串列
return new Gson().fromJson(data, new TypeToken<List<Book>>(){}.getType());
}
}
3、向服務器發送POST請求并獲取回傳資料
注意事項: POST訊息請求頭注明標藍欄位
public class HttpHelper {
//查
public static void listAllBook(Handler handler) {
new Thread(new Runnable() {
@Override
public void run() {
try {
//發送POST請求
OkHttpClient okHttpClient = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder().build();
Request request = new Request.Builder()
.url(Constants.WEB_SITE+"select * from lmj_book")
.addHeader("X-Requested-With", "XMLHttpRequest")
.post(requestBody)
.build();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
//決議服務器回傳json
String res = response.body().string();
//與UI行程通信
handler.obtainMessage(1, BookJsonParser.getInstance().getBook(res)).sendToTarget();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
異步UI重繪
推薦使用android.os.Handler完成執行緒間通信,解決異步界面重繪問題(注意導包不要出現同名異包錯誤),
在資料庫操作執行緒:
//資料查詢
public void listAllBook(Handler handler){
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
List<Book> list = new LinkedList<>();
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USER, PWD);
Statement statement = (Statement) conn.createStatement();
ResultSet res = statement.executeQuery("SELECT * FROM lmj_book");
while (res.next()){
Book book = new Book();
book.setId(res.getInt("_id"));
book.setName(res.getString("bname"));
book.setType(res.getString("type"));
book.setCourse(res.getString("course"));
list.add(book);
}
statement.close();
conn.close();
//利用handler向UI執行緒發送查詢資料
handler.obtainMessage(1, list).sendToTarget();
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
});
thread.start();
}
在UI執行緒:
public class BookListActivity extends BaseActivity {
private BookAdapter adapter;
private ListView listView;
private Handler handler;
private JDBCHelper helper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_list);
adapter = new BookAdapter(this);
helper = JDBCHelper.getInstance();
listView = findViewById(R.id.book_list);
listView.setAdapter(adapter);
handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
//handler接收訊息并處理
adapter.setBookList((List<Book>) msg.obj);
break;
}
}
};
//初始化頁面
initView();
}
private void initView() {
//JDBC獲取資料
helper.listAllBook(handler);
}
public void AddBook(View view) {
navigateTo(com.example.myapp.activity.BookAddActivity.class);
}
public void Refresh(View vi在這里插入代碼片ew) {
reload();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/302534.html
標籤:其他
