Android项目推荐技术总结

虽然之前学习过Android开发的课程,但是真正完成的只有一些小型作业,并没有结合后端来实现一个真正完整的项目,用到的技术与现在的Android开发方法技术都有很大的区别,界面也十分简单单调。

但是在这次项目的实现过程中,我尽力地使用了许多现代Android开发常用的UI框架与API工具。

一、Fragment与Activity

Fragment,意为片段,同传统的Activity不同,它是用户

Fragment 表示 Activity 中的行为或用户界面部分。您可以将多个片段组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个片段。您可以将片段视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除片段(有点像您可以在不同 Activity 中重复使用的“子 Activity”)。

我的理解是,片段就是用户自定义的UI控件,并且是可复用的,可以嵌入不同的Activity中。我认为,现在的Android客户端设计,应该更多地使用Fragment来替代Activity,比如在显示列表的时候,一个ListFragment可以复用在多个Activity中,而不需要创建多个Activity。

Fragment生命周期如下,同Activity是紧密相连的。

Fragment Lifecycle

Fragment & BottomNavigationView

使用底部导航栏,可以说是很多app首页的选择,界面也比较美观。如果没有Fragment,导航栏的点击就又会跳转到不同的页面,加载过程也比较慢,不是很好的选择。

底部导航栏的使用,在activity的xml中声明该空间,同时设置其位置。

1
2
3
4
5
6
7
8
9
10
11
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu" />

添加menu/bottom_nav_menu.xml声明菜单内容。

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/navigation_home"
android:icon="@mipmap/ic_tab_home_page"
android:title="@string/title_home" />
<item
android:id="@+id/navigation_mine"
android:icon="@mipmap/ic_tab_mine"
android:title="@string/title_mine" />
</menu>

在Activity中设置导航栏控制Fragment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
fragmentManager = getSupportFragmentManager();
transaction = fragmentManager.beginTransaction();
switch (item.getItemId()) {
case R.id.navigation_home:
transaction.replace(R.id.fragment_container, TaskListFragment.newInstance(0, "asc"));
transaction.commit();
return true;
case R.id.navigation_mine:
transaction.replace(R.id.fragment_container, MineFragment.newInstance(current_user));
transaction.commit();
return true;
}
return false;
}
};

这里提示,使用newInstance之类的方法来创建Fragment,而不是直接new Fragment()。

Fragment & TabLayout & Viewpaper

TabLayout同底部导航栏相似,只不过它大多数情况下使用在顶部,而配合ViewPaper,能够提前加载Fragment,显示与加载效果将会更好。

其大致使用方法同底部导航栏配合Fragment相似,只不过ViewPaper需要自定义一个ViewPaperAdapter,来控制Fragment的显示。

自定义Adapter类时,需要特别注意getItem方法,可以说是由它来控制显示的Fragment。

1
2
3
4
5
6
7
8
9
10
@Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a TaskDetailFragment (defined as a static inner class below).
if (position == 0) {
return TaskDetailFragment.newInstance(current_task);
} else {
return MessageFragment.newInstance(current_task.getTid().intValue());
}
}

在Activity中设置:

1
2
3
4
5
6
sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
sectionsPagerAdapter.setTask(current_task);
viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);

二、 Retrofit 2 + Rxjava2

Retrofit 2 + Rxjava2这两个工具能够十分方便实现网络api请求和获取,以及相关线程的处理。以下介绍一些简单的使用与可复用模板。

首先添加依赖

1
2
3
4
5
6
7
8
9
10
11
//okhttp和retrofit
implementation 'com.squareup.okhttp3:okhttp:3.2.0'
implementation 'com.squareup.retrofit2:retrofit:2.0.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.0.0'
//retrofit对Rxjava2版本的适配
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
//json解析类
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
//rxjava类
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.4'

添加权限

1
2
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

编写数据实体类,略,只需要注意变量名同json条目名相同,或者添加SerializedName

创建Service类,相当于绑定url吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface ApiService {
@POST("userRegist")
Observable<Status> userRigist(@Query("name") String name,
@Query("phone") String phone,
@Query("email") String email,
@Query("password") String password);

@POST("userLogin")
Observable<Status> userLogin(@Query("name") String name,
@Query("password") String password);

@POST("getUser")
Observable<Status> getUser(@Query("name") String name);

@POST("updateUser")
Observable<Status> updateUser(@Query("name") String name,
@Query("phone") String phone,
@Query("email") String email,
@Query("password") String password,
@Query("money") int money);
// ...other API

}

封装Retrofit请求过程,相当于封装一个api请求工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Api {

public static String baseUrl = "http://111.230.13.139:8080/ServerAndDB/";

public static ApiService apiService;
//单例
public static ApiService getApiService() {
if (apiService == null) {
synchronized (Api.class) {
if (apiService == null) {
new Api();
}
}
}
return apiService;
}

private Api() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())//请求的结果转为实体类
//适配RxJava2.0,RxJava1.x则为RxJavaCallAdapterFactory.create()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
apiService = retrofit.create(ApiService.class);
}

}

封装线程管理和订阅,相当于将线程管理函数封装,并且封装相应的方法用于调用url。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class ApiMethods {
/**
* 封装线程管理和订阅的过程
*/
public static void ApiSubscribe(Observable observable, Observer observer) {
observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}

/**
* @param observer 由调用者传过来的观察者对象
*/
public static void userRegist(Observer<Status> observer, String name, String phone, String email, String password) {
ApiSubscribe(Api.getApiService().userRigist(name, phone, email, password), observer);
}

public static void updateUser(Observer<Status> observer, String name, String phone, String email, String password, int money) {
ApiSubscribe(Api.getApiService().updateUser(name, phone, email, password, money), observer);
}

public static void userLogin(Observer<Status> observer, String name, String password) {
ApiSubscribe(Api.getApiService().userLogin(name, password), observer);
}

public static void getUser(Observer<Status> observer, String name) {
ApiSubscribe(Api.getApiService().getUser(name), observer);
}

}

重写Observer类与监听接口,将返回值封装并创建可复用接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public interface ObserverOnNextListener<T> {
void onNext(T t);
}

public class MyObserver<T> implements Observer<T> {
private static final String TAG = "MyObserver";
private ObserverOnNextListener listener;
private Context context;

public MyObserver(Context context, ObserverOnNextListener listener) {
this.listener = listener;
this.context = context;
}

@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "onSubscribe: ");
//添加业务处理
}

@Override
public void onNext(T t) {
listener.onNext(t);
}

@Override
public void onError(Throwable e) {
Log.e(TAG, "onError: ", e);
//添加业务处理
}

@Override
public void onComplete() {
Log.d(TAG, "onComplete: ");
//添加业务处理
}
}

调用:重写onNext,并调用ApiMethod中相应函数即可。

三、推荐一些UI控件

Nice Spnnier

源地址

Android原生的下拉列表十分的简陋,这个下拉UI要好看很多,使用也十分简单。

alt tag

Time Picker Dialog

源地址

在时间选择的处理上,原生的DatePicker和TimePicker不仅需要分开使用,而且也不是很好看,这个时间选择对话框的效果要好很多。

img

Reference