第一行程式碼——第三章:軟體也要拼臉蛋——UI開發的點點滴滴
目錄:
知識點:
3.1 如何編寫程式介面
Android中有多種編寫程式介面的方式可供選擇。Android Studio 和Eclipse中都提供了相應的視覺化編輯器,允許使用拖放控制元件的方式來編寫佈局,並能在檢視上直接修改控制元件的屬性。不過我並不推薦你使用這種方式來編寫介面,因為視覺化編輯工具並不利於你去真正瞭解介面背後的實現原理。通過這種方式製作出的介面通常不具有很好的螢幕適配性,而且當需要編寫較為複雜的介面時,視覺化編輯工具將很難勝任。因此本書中所有的介面都將通過最基本的方式去實現,即編寫XML程式碼。等你完全掌握了使用XML來編寫介面的方法之後,不管是進行高複雜度的介面實現,還是分析和修改當前現有介面,對你來說都將是手到擒來。
3.2 常用控制元件的使用方法
3.2.1TextView
顯示一段文字資訊
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="HelloWord"
/>
3.2.2 Button
使用者互動的控制元件(比如,點選跳轉)
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Im Button"
/>
3.2.3 EditText
輸入 和編輯內容(如:微信傳送的訊息)
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="請輸入內容"
/>
3.2.4 ImageView
圖片控制元件 用於顯示圖片
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
/>
3.2.5 ProgressBar
進度條
Style不寫 預設 圓形進度條 ?android:attr/progressBarStyleHorizontal
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal"
/>
3.2.6 AlertDialog
可以在當前的介面彈出一個對話方塊
new AlertDialog.Builder(this)
.setTitle("確定要刪除嗎?")
.setMessage("Something important.")
.setPositiveButton("確定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
})
.show();
3.2.7 ProgressDialog
進度條彈框
final ProgressDialog dialog = new ProgressDialog(this);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);// 設定水平進度條
dialog.setCancelable(true);// 設定是否可以通過點選Back鍵取消
dialog.setCanceledOnTouchOutside(false);// 設定在點選Dialog外是否取消Dialog進度條
dialog.setIcon(R.drawable.ic_launcher);// 設定提示的title的圖示,預設是沒有的
dialog.setTitle("提示");
dialog.setMax(100);
dialog.setButton(DialogInterface.BUTTON_POSITIVE, "確定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
});
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, "取消",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
});
dialog.setButton(DialogInterface.BUTTON_NEUTRAL, "中立",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
});
dialog.setMessage("這是一個水平進度條");
dialog.show();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
int i = 0;
while (i < 100) {
try {
Thread.sleep(200);
// 更新進度條的進度,可以在子執行緒中更新進度條進度
dialog.incrementProgressBy(1);
// dialog.incrementSecondaryProgressBy(10)//二級進度條更新方式
i++;
} catch (Exception e) {
// TODO: handle exception
}
}
// 在進度條走完時刪除Dialog
dialog.dismiss();
}
}).start();
3.3 詳解4種常用佈局
3.3.1 線性佈局
LinearLayout又稱作線性佈局,是一種非常常用的佈局。
android:orientation="horizontal" 預設水平 vertical 修改後橫向
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
3.3.2 相對佈局
可以通過相對定位的方式讓空間出現在佈局的任何位置。
如下讓Button2 在Button1 右側
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_1"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_2"
android:layout_toRightOf="@+id/btn_1"
/>
</RelativeLayout>
3.3.3 幀佈局
應用場景也少了很多,所有控制元件都會顯示在佈局的左上角
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_1"
/>
</FrameLayout>
3.3.4 百分比佈局
前面3 種佈局都是從Android 1.0版本中就開始支援了,一直沿用到現在,但是特殊情況以上佈局實現起來很不支援的
百分比庫 地址 使用方式 請同時檢視
https://github.com/JulienGenoud/android-percent-support-lib-sample
3.4 系統控制元件不夠用?建立自定義控制元件
當系統自帶的控制元件不能支援我們的需求時,就需要自定義空控制元件了。
3.4.1 引入佈局
3.4.2 建立自定義控制元件
這2章主要是講 新建一個xml檔案 設定標題欄的佈局
並不是我們所想的自定義View
3.5 最常用和最難用的控制元件——ListView
由於螢幕控制元件比較有限,能夠一次性在螢幕上顯示的內容不多,所以有大資料的時候需要用到列表
3.5.1 ListView的簡單用法
在佈局中加入ListView 控制元件,併為ListView 指定了一個id 設定成match_parent 佔滿整個空間
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/list_view"
>
</ListView>
在Activity中
public class MainActivity extends Activity {
private String[] data = { "Apple", "Banana", "Orange", "Watermelon",
"Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango" };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
MainActivity.this, android.R.layout.simple_list_item_1, data);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
}
在這裡我運用了系統包含的一個TextView的佈局檔案:android.R.layout.simple_expandable_list_item_1,呼叫這個比較方便,
ArrayAdapter<String> adapter = new ArrayAdapter<String>( MainActivity.this, android.R.layout.simple_list_item_1, data); 的意思是:建立一個數組介面卡的程式碼,裡面有三個引數,第一個引數是上下文,就是當前的Activity, 第二個引數是android sdk中自己內建的一個佈局,它裡面只有一個TextView,這個引數是表明我們陣列中每一條資料的佈局是這個view,就是將每一條資料都顯示在這個 view上面;第三個引數就是我們要顯示的資料。listView會根據這三個引數,遍歷data裡面的每一條資料,讀出一條,顯示到第二 個引數對應的佈局中,這樣就形成了我們看到的listView.
ArrayAdapter是BaseAdapter的子類
3.5.2 定製ListView的介面
定義一個實體類Fruit
public class Fruit {
private String name;
private int imageId;
public Fruit(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}
public String getName() {
return name;
}
public int getImageId() {
return imageId;
}
}
為 ListView 的子項指定一個我們自定義的佈局 fruit_item.xml。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dip" />
</LinearLayout>
建立一個自定義的介面卡 FruitAdapter,這個介面卡繼承自 ArrayAdapter。重寫構造方法和 getView 方法。
public class FruitAdapter extends ArrayAdapter{
private final int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = (Fruit) getItem(position); // 獲取當前項的Fruit例項
View view = LayoutInflater.from(getContext()).inflate(resourceId, null);//例項化一個物件
ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);//獲取該佈局內的圖片檢視
TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);//獲取該佈局內的文字檢視
fruitImage.setImageResource(fruit.getImageId());//為圖片檢視設定圖片資源
fruitName.setText(fruit.getName());//為文字檢視設定文字內容
return view;
}
}
View view = LayoutInflater.from(getContext()).inflate(resourceId, null);使用Inflater物件來將佈局檔案解析成一個View
在MainActivity中編寫,初始化水果資料
public class MainActivity extends Activity {
private List<Fruit> fruitList = new ArrayList<Fruit>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits(); // 初始化水果資料
FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
private void initFruits() {
Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear", R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit("Grape", R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
fruitList.add(mango);
}
}
執行效果圖
這樣一個簡單的ListView介面就完成了
3.5.3 提升ListView的執行效率
- 新增加一個內部類ViewHolder,對控制元件進行快取。
- 複用convertView
- 減少findViewById的次數。
3.5.4 ListView的點選事件
以下為單個item點選事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
}
});
如果要實現子view點選事件 可以寫介面實現
3.6 更強大的滾動控制元件——RecyclerView
增加版的ListView優化Listview中的各種問題,擴充套件性要好。
3.6.1 RecyclerView的基本用法
沒有新增build gradle 的 新增依賴庫
版本號 可改
implementation 'com.android.support:recyclerview-v7:27.1.1'
新增RecyclerView
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
同上使用水果類就可以
新建Adapter
我這裡用了ButterKnife 註解
package com.dak.administrator.firstcode.material_design;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.dak.administrator.firstcode.R;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Created by Administrator on 2018/11/7.
*/
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private Context mContext;
private List<Fruit> mFruitList;
public FruitAdapter(List<Fruit> fruitList) {
mFruitList = fruitList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (mContext == null) {
mContext = parent.getContext();
}
View view = LayoutInflater.from(mContext).inflate(R.layout.fruit_item, parent, false);
final ViewHolder holder = new ViewHolder(view);
holder.cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int postion = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(postion);
Intent intent = new Intent(mContext, CollapseActivity.class);
intent.putExtra(CollapseActivity.FRUIT_NAME, fruit.getName());
intent.putExtra(CollapseActivity.FRUIT_IMAGE_ID, fruit.getImageId());
mContext.startActivity(intent);
}
});
return holder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitName.setText(fruit.getName());
Glide.with(mContext).load(fruit.getImageId()).into(holder.fruitImage);
}
@Override
public int getItemCount() {
return mFruitList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder{
@BindView(R.id.fruit_image)
ImageView fruitImage;
@BindView(R.id.fruit_name)
TextView fruitName;
@BindView(R.id.card_view)
CardView cardView;
public ViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
}
}
}
Activity呼叫
adapter = new FruitAdapter(fruitsList);
GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
3.6.2 實現橫向滾動和瀑布流佈局
橫向滾動只要把 之前GridLayoutManager 改成如下即可
LinearLayoutManager預設 豎向
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
瀑布流:
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
當然瀑布流是存在著許多坑 請看
https://www.jianshu.com/p/b0f80b1c29d0
https://blog.csdn.net/lhk147852369/article/details/84346240
3.6.3 RecyclerView的點選事件
點選事件已經在上程式碼加上了 在ViewHolder當中 這是在adapter的
如果需要在Activity或其他地方 可以寫介面實現,這裡不說了。
http://www.jcodecraeer.com/plus/view.php?aid=7881
3.7 編寫介面的最佳實踐
3.7.1 製作Nine-Patch圖片
https://blog.csdn.net/lhk147852369/article/details/84346031
3.7.2 編寫精美的聊天佈局
主要是在onBindViewHolder 判斷傳過來的資訊是傳送的還是接收的
通過Type值 去設定Visible 或者Gone
當然還有另外方法
通過getItemViewType方法
https://www.cnblogs.com/android-blogs/p/5690853.html
3.8 小結與點評
郭霖總結:
雖然本章的內容很多,但我覺得學習起來應該還是挺愉快的吧。不同於上一章中我們來來回回使用那幾個按鈕,本章可以說是使用了各種各樣的控制元件,製作出了豐富多彩的介面。尤其是在實戰環節,編寫出了那麼精美的聊天介面,你的滿足感應該比上一章還要強吧?
本章從Android中的一些常見控制元件開始人手,依次介紹了基本佈局的用法、自定義控制元件的方法、ListView 的詳細用法以及RecyclerView的使用,基本已經將重要的UI知識點全部覆蓋了。想想在開始的時候我說不推薦使用視覺化的編輯工具,而是應該全部使用XML的方式來編寫介面,現在你是不是已經感覺使用XML非常簡單了呢?以後不管面對多麼複雜的介面,我希望你都能夠自信滿滿,因為真正理解了介面編寫的原理之後,是沒有什麼能夠難得倒你的。
不過到目前為止,我們還只是學習了Android 手機方面的開發技巧,下一章將會涉及一些Android平板方面的知識點,能夠同時相容手機和平板也是自Android4.0 系統開始就支援的特性。適當地放鬆和休息一段時間後, 我們再來繼續前行吧!
我的總結:
這章還是比較重要的,因為在工作中接觸到的很多都是和列表有關的。