12 Jul 2018
outline
- Basic Setting
- Case 1 : Re-use
- Case 2 : Listener
- Case 3 : String concat
- Case 4 : hide widget
Basic Setting
Gradle
android {
....dataBinding {
enabled = true
}
}
xml : activity_info.xml
<layout>
<LinearLayout
</LinearLayout>
</layout>
Bind
ActivityInfoBinding mBinding
= DataBindingUtil.setContentView(this, R.layout.activity_info);
Usage
Binding Data
Object (Kotlin)
@SuppressLint("ParcelCreator")
@Parcelize
class User(
@SerializedName("name")
var mName: String?
): Parcelable
xml
<layout>
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
</LinearLayout>
</layout>
bind
mBinding.setUser(getUser());
Usage
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.MName}"/>
Case 1 : Re-use
idea

Just do it
- LinearLayout(horizontal)
- LinearLayout(vertical)
- ImageView
- android:src=”@drawable/……”
- TextView
- android:text=”@string/….”
- LinearLayout(vertical)
- ImageView
- android:src=”@drawable/……”
- TextView
- android:text=”@string/….”
- LinearLayout(vertical)
- ImageView
- android:src=”@drawable/……”
- TextView
- android:text=”@string/….”
- LinearLayout(vertical)
- ImageView
- android:src=”@drawable/……”
- TextView
- android:text=”@string/….”
重複性太高
include xml (或是使用merge)
original xml
- LinearLayout(horizontal)
- include
- include
- include
- include
activity have to handle the change
mBinding.layoutAb.image.setImageDrawable(R.drawable.xxx);
mBinding.layoutAb.textView.setText(getString(R.string.xxx));
mBinding.layoutCd.image.setImageDrawable(R.drawable.xxx);
mBinding.layoutCd.textView.setText(getString(R.string.xxx));
mBinding.layoutEf.image.setImageDrawable(R.drawable.xxx);
mBinding.layoutEf.textView.setText(getString(R.string.xxx));
mBinding.layoutGh.image.setImageDrawable(R.drawable.xxx);
mBinding.layoutGh.textView.setText(getString(R.string.xxx));
another way
inclucde xml
<data>
<variable name="text" type="String"/>
<variable name="icon" type="android.graphics.drawable.Drawable"/>
</data>
TextView :
ImageView :
orginal xml
app:icon="@{@drawable/myDrawable}"
app:text="@{@string/myText}"
result
- LinearLayout(horizontal)
- include
- android:id=”@+id/….”
- app:icon=”@{@drawable/myDrawable}”
- app:text=”@{@string/myText}”
Timing
Case 2 : Listener
xml
- LinearLayout(horizontal)
- include
- android:id=”@+id/layoutInclude1
- include
- android:id=”@+id/layoutInclude2
- include
- android:id=”@+id/layoutInclude3
- include
- android:id=”@+id/layoutInclude4
setOnClickListener
X
mBinding.layoutInclude1.setOnClickListener
O
findViewById(R.id.layoutInclude1).setOnClickListener();
another way
include xml
1.
<variable name="handler" type="XXXXXX.MainActivity"/>
2.
<variable name="handler" type="yourHandler"/>
android:onClick=”@{handler::onFeaturesClick}”
result in Activity
mBinding.layoutInclude1.setHandler(this);
public void onFeaturesClick(View view){}
advantage
Collect all handler together
Case 3 : String concat
Example
地址:XXXXXX
First way
<TextView
android:text="@string/address"/>
<TextView
android:text="@{info.address}"/>
second way
<TextView
android:id="@+id/textAddress"
android:text=""/>
mBinding.textAddress.setText(
getString(R.string.address) + info.address);
third way
<TextView
android:id="@+id/textAddress"
android:text="@{@string/address + info.address}"/>
another Example
output:
2018/2/15 16:48
mBinding.textTime.setText(
info.date + " " + info.time);
you can write
<TextView
android:text="@{info.date + ` ` + info.time}"/>
little conclusion
1.顯示邏輯到底要寫在UI中還是必須拉到acitvity中
2.都寫在UI中會不會造成後面閱讀的人的困擾
solution
android:visibility="@{isVisible ? View.VISIBLE : View.GONE}"
11 Dec 2017
設定觸控時監聽
imageView.setOnTouchListener(imgListener);
計算出 actionBar長度以供設定圖片位置
Hint : 可將 actionBarHeight
存在全域
public void getActionBarHeight() {
TypedValue tv = new TypedValue();
if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
}
監聽拖曳動作並重設位置
private View.OnTouchListener imgListener = new View.OnTouchListener() {
private float lengthXFromLeftToTouchEvent, lengthYFromTopToTouchEvent;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//計算點下去的位置到圖片左上角位置的距離
lengthXFromLeftToTouchEvent = event.getRawX() - imageView.getX();
lengthYFromTopToTouchEvent = event.getRawY() - imageView.getY() - actionBarHeight;
break;
case MotionEvent.ACTION_MOVE:
mx = (int) (event.getRawX() - lengthXFromLeftToTouchEvent);
my = (int) (event.getRawY() - lengthYFromTopToTouchEvent - actionBarHeight);
v.layout(mx, my, mx + v.getWidth(), my + v.getHeight());
break;
case MotionEvent.ACTION_UP:
imageView.layout(mx, my, mx + imageView.getWidth(), my + imageView.getHeight());
break;
}
return true;
}
};
離開app時儲存資訊
@Override
protected void onPause() {
super.onPause();
sharedpreferencesEditor.saveInt("lengthXFromLeftToTouchEvent", mx);
sharedpreferencesEditor.saveInt("lengthYFromTopToTouchEvent", my);
}
進入app時重設位置
public void initView() {
imageView = findViewById(R.id.imageView);
mx = sharedpreferencesEditor.readInt("lengthXFromLeftToTouchEvent");
my = sharedpreferencesEditor.readInt("lengthYFromTopToTouchEvent");
imageView.layout(mx, my, mx + imageView.getWidth(), my + imageView.getHeight());
}
補充
這裡提供兩種設定圖片位置的方法,但是不能混用
v.layout(mx, my, mx + v.getWidth(), my + v.getHeight());
09 Dec 2017
Outline
- What is Retrofit?
- Before start
- Http libraries
- Before start
- Http libraries
- OKhttp
- Retrofit
- Q&A
What is Retrofit?
Retrofit turns your HTTP API into a Java interface.
Before start
- RESTful API
- JSON
- Gson
- Annotation
RESTful API
rule or concept
- 每一個URI代表一種資源
- 客戶端通過四個HTTP動詞,對服務器端資源進行操作
@GET("users/list")
@GET("user/{userid}")
JSON & Gson
public static <T> T fromJson(String json, Class<T> klass) {
return new Gson().fromJson(json, klass);
}
public static <T> List<T> fromJsonArray(String json, Class<T[]> klass) {
T[] objects = new Gson().fromJson(json, klass);
return objects == null ? new ArrayList<>()
: new ArrayList<T>(Arrays.asList(objects));
}
public static String toJson(Object object) {
return new GsonBuilder()
.create()
.toJson(object);
}
public String toJson() {
return new Gson().toJson(this);
}
Annotation
Usage
- Dagger
- Retrofit
- Gson
- DBflow
- Butterknife
How to build Annotation
@Bind(R.id.text)
TextView text;
- build a Annotation class
- write a Singleton class to handle TODO
- inject the class when activity oncreate
preference : https://b013040034.github.io/ohmyblog/android/2018/02/06/annotation.html
Http libraries
- HttpClient
- OkHttp
- Volley
- retrofit
OKHttp
Find the bug 1
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
OkHttpClient client = new OkHttpClient();
String url = MessageFormat.format(ApiClient.Postal, 408);
final Request request = new Request.Builder()
.url(ApiClient.BASE_URL + url)
.build();
client.newCall(request).execute().body().string();
} catch (IOException e) {
e.printStackTrace();
}
}
Error Msg 1
android.os.NetworkOnMainThreadException
Resolve 1
- build a Singleton
- init okhttp in constuctor
- write a method to handle request
public void getData(String url, Callback callback) {
final Request request = new Request.Builder()
.url(BASE_URL + url)
.build();
client.newCall(request).enqueue(callback);
}
Callback -> Find the bug 2
apiClient.getDataWithCallBack(url, new Callback() {
@Override
public void onFailure(Call call, final IOException e) {
text.setText(e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String resStr = response.body().string();
text.setText(resStr);
}
});
Error Msg 2
Only the original thread that created a view hierarchy can touch its views.
AsyncTask
new AsyncTask<String, Void, String>() {
@Override
protected String doInBackground(String... urls) {
try {
return apiClient.getDataWithAsync(url);
} catch (IOException e) {
return e.getMessage();
}
}
@Override
protected void onPostExecute(String resStr) {
text.setText(resStr);
}
};
Okhttp Conclusion
- you have to make request in work thread or use callback
- you have to change to ui thread after callback
–> thread safe
–> Encapsulation 封裝
Encapsulation
Volley vs Retrofit
- Gson
Get : call -> get object
Post : make object -> call
- Restful Api
easy way to set url and data
What I expect
Lazy Lazy Lazy Lazy
- No need to custom request
-> No need to encapsulation
- Api class looks like Api Document
-> easy to find & add & fix api
- get object and post object
Retrofit
dependencies
dependencies {
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
}
Website
https://github.com/square/retrofit.
Singleton
public class ApiClient {
public static final String BASE_URL = "https://api/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Api : Get
public interface ApiService {
@GET("weather/{lat}/{lng}")
Call<WeatherData> getWeather(@Path("lat") String lat, @Path("lng") String lng);
}
Api : Get (Volley and okHttp)
public static final String coordinate = "weather/{0}/{1}";
String url = MessageFormat.format(ApiClient.coordinate, lat, lng);
Convert json to Object
- Website
http://www.jsonschema2pojo.org/
- Plugins

Object
public class WeatherData implements Serializable {
@SerializedName("location")
public String location;
@SerializedName("temp")
public float temperature;
}
You can use Kotlin + paracle instead
Get data
public void getWeatherData() {
Retrofit retrofit = ApiClient.getClient();
apiService = retrofit.create(ApiService.class);
Call<WeatherData> data = apiService.getWeather("24","120");
data.enqueue(new Callback<WeatherData>() {
@Override
public void onResponse(Call<WeatherData> call, Response<WeatherData> response) {
WeatherData weatherData = response.body();
}
@Override
public void onFailure(Call<WeatherData> call, Throwable t) {
}
});
}
Api : Post
public interface ApiService {
@POST("AccessToken")
Call<AccessToken> getTokenDebug(@Body AccessTokenInput tokenInput);
}
Post data
Call<AccessToken> dataDebug = apiService.getTokenDebug(accessToken);
dataDebug.enqueue(new Callback<AccessToken>() {
@Override
public void onResponse(Call<AccessToken> call, Response<AccessToken> response) {
AccessToken accessToken = response.body();
caculateResult(accessToken, textResultDebug);
}
@Override
public void onFailure(Call<AccessToken> call, Throwable t) {
textResultDebug.setText(t.getMessage());
}
});
Api : Post (Volley)
- StringRequest : with param
- JsonObjectRequest : with json
Query String
@GET("news")
Call<News> getItem(@Query("type") String type);
@GET("news")
Call<News> getItem(@QueryMap Map<String, String> map);
Post with field
@FormUrlEncoded
@POST("news")
Call<User> postItem(@Field("type") String type);
@FormUrlEncoded
@POST("news")
Call<User> postItem(@FieldMap Map<String, String> map);
Other features
- 簽名 : Signature
- 標頭 : Headers
- 檔案 : Multipart
signature
https://github.com/square/okhttp/wiki/Interceptors
Multipart
https://chenkaihua.com/2016/04/02/retrofit2-upload-multipart-files/
perference
http://square.github.io/retrofit/
https://ihower.tw/blog/archives/1542
https://itw01.com/V33WE9D.html
https://bng86.gitbooks.io/android-third-party-/content/retrofit.html
28 Nov 2017
Gradle
dependencies {
compile 'com.github.bumptech.glide:glide:4.0.0-RC1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0-RC1'
}
Website
https://github.com/bumptech/glide.
Code
public static void setDrawableIntoImageView(Context context, int drawable, ImageView imageView) {
Glide.with(context)
.load(drawable)
.into(imageView);
}
public static void setUriIntoImageView(Context context, Uri uri, ImageView imageView) {
Glide.with(context)
.load(uri)
.into(imageView);
}
public static void setUrlIntoImageView(Context context, String url, ImageView imageView) {
Glide.with(context)
.load(url)
.into(imageView);
}