YH Lin
11 min readFeb 18, 2021

USR 整合

先劇透一下這次要做的例子

前製作業:

1.先準備好圖檔,放在res/drawable 目錄下

撰寫公用Application,裡面紀錄要連線的url,並儲存使用者資訊

在gradle 中加上

implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.auth0.android:jwtdecode:2.0.0'

設定manifest檔案

先加網路權限,調整application

<uses-permission android:name="android.permission.INTERNET" /><application
android:name=".MainApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.UsrApp">
<activity android:name=".CheckActivity"></activity>
<activity android:name=".Menu" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

在res/xml 下撰寫network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>

撰寫網路連線類別

因為這個專案會很常用到HttpUrlConnection 的post 跟get 方法,所以先把它們抽出來寫成一個類別

開始撰寫主頁的UI,用RelativeLayout 配置,LinearLayout 向下對齊,ImageView 沿著LinearLayout 上方對齊. 利用ImageView 的src 帶出預設背景圖

在MainActivity中,輸入帳號密碼認證後,利用UsrPost 取得物件,利用帳號密碼的物件取得token後將token 存起來,並將token轉成user_id,如果順利取得token 後就可以切換下一頁

第2頁產生一個MENU的Activity,Layout 一樣用RelativeLayout

在第三顆按鈕按下去後執行

public void doCheck(View view) {
Intent it = new Intent(this,CheckActivity.class);
startActivity(it);
}

再開一個checkActivity,Layout 配置以Layout 為主,內含一個RecyclerView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CheckActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/checkview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

</LinearLayout>

待會的adapter 要搭的check_item.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="10dp">
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">

<TextView
android:id="@+id/tv_activity_title"
android:textSize="36dp"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="100dp"/>
</LinearLayout>

</androidx.cardview.widget.CardView>

在checkActivity中OnCreate方法

LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(RecyclerView.VERTICAL);
view.setLayoutManager(manager);
view.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
app = (MainApp) getApplication();

然後搭配一個執行緒取得後端Activity的活動名稱跟點數,

new Thread(new Runnable() {
@Override
public void run() {
try {
UsrGet<Check> usrGet = new UsrGet<>(app.activity_url);
usrGet.setToken(app.getToken());
String response = usrGet.doGet();
Type type = new TypeToken<List<Check>>(){}.getType();
List<Check> checks = usrGet.parseJsonList(response, type);
handler.setData(checks);
handler.sendEmptyMessage(0);

} catch (IOException e) {
for (StackTraceElement se:e.getStackTrace())
{app.Log(se.toString());}
}

}
}).start();

透過handler 來處理RecyclerView的UI,在處理handleMessage中會使用到recycleview.setAdapter()來綁訂UI 界面

class MyHandler extends Handler{
List<Check> datas;
public void setData(List<Check> datas)
{
this.datas = datas;
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
view.setAdapter(new CheckAdapter(CheckActivity.this,datas));
// view.notifyAll();
}
}

在Adapter中繼承RecyclerView.Adapter 預設建構子傳入一個context ,與List<Check> datas

public CheckAdapter(Context context,List<Check> datas)
{
this.datas = datas;
this.context = context;
app = (MainApp) ((CheckActivity)context).getApplication();
}

撰寫ViewHolder

class ViewHolder extends RecyclerView.ViewHolder{
TextView tv_activity_title;
public ViewHolder(@NonNull View itemView) {
super(itemView);
tv_activity_title= itemView.findViewById(R.id.tv_activity_title);

}
}

在onCreateViewHolder中綁定checkitem

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Context c = parent.getContext();
View v = LayoutInflater.from(c).inflate(R.layout.checkitem, parent,false);
ViewHolder vh = new ViewHolder(v);
return vh;
}

撰寫getItemCount

@Override
public int getItemCount() {
return datas.size();
}

撰寫onBindViewHolder,利用setText 秀出值

holder.tv_activity_title.setText(datas.get(position).getTitle());

因為這邊要做的是按下按鈕後將資料送到後端,因此會有一個run 方法待完成

public void run() {
try {
UsrPost usrPost = new UsrPost(app.getMy_activity_url());
usrPost.setToken(app.getToken());
String response = usrPost.doPost(json);
Looper.prepare();
Snackbar.make(holder.itemView, datas.get(position).getTitle()+"已上傳", Snackbar.LENGTH_LONG).show();
Looper.loop();


// Toast.makeText(context,"檔案已上傳",Toast.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
}
}

完整程式

No responses yet