Android第一行代碼讀書筆記 - 第三章

====================================

====== 第三章:UI開發(fā)的點點滴滴 ======

====================================

界面設計和功能實現(xiàn)同樣重要。界面美觀可以增加用戶粘性养涮。

1罪针、如何編寫程序界面简卧。

我們不建議使用可視化見面來進行拖拽編寫布局熟呛,因為可視化編輯不利于你去真正了解界面背后的實現(xiàn)原理藕帜。通過可視化界面寫出的界面通常不具備很好的屏幕適配性。并且編寫復雜界面時無法勝任栓始。因此本書的所有界面都是通過編寫xml代碼來實現(xiàn)的胸完。

2书释、重用控件的使用方法:

新建一個UIWidgetTest項目。

2.1 TextView:聽說是最簡單的控件赊窥,顯示一段文本信息爆惧。修改activity_main.xml中代碼如下:

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android

android:orientation=“vertical”

android:layout_width=“match_parent”

android:layout_height=“match_parent”>

<TextView

android:id=“@+id/text_view”

android:layout_width=“match_parent”

android:layout_height=“wrap_context”

android:text=“This is TextView” />

</LinearLayout>

match_parent、fill_parent锨能、wrap_content都是控件的可選屬性扯再。其中芍耘,match_parant和fill_parent的意義相同,官方更加推薦match_parent熄阻。wrap_context表示讓當前控件的大小剛好可以包住里面的內容斋竞,也就是控件內容決定當前控件的大小。當然你也可以指定控件一個特定值的寬高的大小秃殉。(但是這樣可能會出現(xiàn)屏幕的適配問題)

如需修改textview里面的文字屬性坝初,增加android:textSize=“24sp” android:textColor=“#00ff00”

Android中字體大小使用sp作為單位。

3.2.2 Button

添加一個Button钾军,

<Button

android:id=“@+id/button”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

andoird:text=“Button” />

我們在布局文件里面設置的文字是Button鳄袍,但是最終顯示結果卻是BUTTON,這是由于系統(tǒng)會對button中的所有英文字母自動進行大寫轉換吏恭,如果這不是想要的效果拗小,可以使用如下配置來禁用這一默認特新:Android:textAllCaps=“false”

接著,在MainActivity中添加按鈕的點擊邏輯

Public class MainActivity extends AppCpmpatActivity {

@ovrride

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Button button = (Button)findViewById(R.id.button);

button.setOnClickListener(new View.onClickListener() {

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.button: {

// 點擊按鈕的邏輯

}break;

default:

break;

}

}

});

}

}

如果不喜歡使用匿名類的方式來注冊監(jiān)聽器砸泛,也可以使用實現(xiàn)接口的方式來進行注冊

Public class MainActivity extends AppCompatActivity implements View.OnClickListener {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Button button = (Button)findViewById(R.id.button);

button.setOnClickListener(this);

}

@Override

public void onClick(View v) {

switch(v.getId()) {

case R.id.button : {

// 點擊邏輯

}break;

default:

break;

}

}

3.2.3 EditText(輸入框)

一樣的十籍,android:hint=“placeholder text”, android:maxLines=“2”

3.2.4 ImageView

顯示圖片的控件,圖片通常都是放在drawable開頭的目錄下

創(chuàng)建一個drawable-xhdpi的文件夾唇礁,放入兩張圖片

<ImageView

android:id=“@+id/image_view”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:src=“@drawable/img_1” />

3.2.5 ProcessBar(中間圓圈進度旋轉)

<ProcessBar

android:id=“@+id/progress_bar”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

style=“?android:attr/progressBarStyleHorizontal”

android:max=“100” />

所有控件都可以設置一個屬性visibility,可選值為visible可見的(默認值)惨篱、invisible不可見和gone不僅不可見而且不占任何屏幕控件盏筐。我們還可以通過代碼來設置控件的可見性,使用的是視圖的setVisibility()砸讳,可以傳入View.VISIBLE琢融、View.INVISIBLE、View.GONE

if (progressBar.getVisibility() == View.GONE) {

progressBar.setVisibility(View.VISIBLE);

} else {

progressBar.setVisibility(View.GONE);

}

// 當修改了progressBar的樣式之后簿寂,修改如下代碼漾抬,就可以點擊button的時候讓progress一點點增加

int progress = progressBar.getProgress();

progress += 10;

progressBar.setProgress(progress);

3.2.6 AlertDialog

AlertDialog可以在當前界面彈出一個對話框,并且是置頂于所有控件之上常遂,能夠屏蔽掉其他控件的交互能力纳令,因此AlertDialog一般都是用于提示一些非常重要的內容或者警告信息。

我們現(xiàn)在可以在MainActivity中寫上如下代碼克胳。

public void onClick(View v) {

switch (v.getId()) {

case R.id.button: {

AlertDialog.Bulder dialog = new AlertDialog.Bulder(MainActivity.this);

dialog.setTitle(“This is Dialog”);

dialog.setMessage(“Something important.”);

dialog.setCancelable(false);

dialog.setPositiveButton(“OK”, new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

}

});

dialog.setNegativeButton(“Cancel”, new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

}

});

dialog.show();

break;

default:

break;

}

}

}

setPositiveButton()方法為對話框設置確定按鈕點擊事件平绩,setNegativeButton()方法設置取消按鈕點擊事件。最后調用show()方法顯示出來漠另,

3.2.7 ProgressDialog(貌似現(xiàn)在已經取消使用了)

與AlertDialog類似捏雌,但是會有一個進度條,一般表示當前操作比較耗時笆搓,讓用戶耐心等待

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

@Override

public void onClick(View v) {

Switch (v.getId()) {

case R.id.button: {

ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);

progressDialog.setTitle(“This is ProgressDialog”);

progressDialog.setMessage(“Loading…”);

progressDialog.setCancelable(ture);

progressDialog.show();

}break;

default:

break;

}

}

設置setCancelable()傳入false性湿,表示不能通過back鍵取消掉纬傲。

3.3 詳解4種基本布局。(新的測試項目UILayoutTest)

布局的內部除了放置控件外肤频,還可以放置布局嘹锁,通過多層布局的嵌套,我們就能夠完成一些比較復雜的界面實現(xiàn)着裹。

Android中包括四種布局:線性布局领猾、相對布局、幀布局骇扇、百分比布局

3.3.1 線性布局

線性布局LinearLayout摔竿,將他所包含的控件在線性方向上依次排列。之前我們用的一直都是這個線性布局少孝,可以通過修改adroid:orientation屬性指定排列方向是vertival為垂直方向继低,horizontal為水平方向。

<LinearLayout xmlns:android:=“http://schemas.android.com/apk/res/android

android:orientation=“vertical”/horizontal

android:layout_width=“match_parent”

android:layout_height=“match_parent” >

</ LinearLayout>

需要注意的是稍走,如果排列方式設置為horizontal水平方向袁翁,內部的控件就絕對不能將寬度指定為match_parent,因為這樣的話婿脸,單獨一個控件就占滿整個水平方向了粱胜,其他控件就沒有位置可以放置了。同樣道理狐树,如果排列方向設置為vertival垂直方向焙压,那么內部控件的高度就不能設置為match_parent。

android:layout_gravity屬性用于指定文字在控件中的對齊方式抑钟。

Android:layout_weight使用比例的方式來指定控件的大醒那(在手機屏幕的適配性方便非常重要)

案例:

<EditText

android:id=“@+id/input_message”

android:layout_width=“0dp”

android:layout_height=“wrap_content”

android:layout_weight=“1”

android:hint=“Type something” />

<Button

android:id=“@+id/send”

android:layout_width=“0dp”

android:layout_height=“wrap_content”

android:layout_weight=“1”

android:text=“Send” />

這里雖然設置了android:layout_width=“0dp”,但是由于設置了比重在塔,以比重為準幻件,0dp是比較標準的寫法。

比重就是總數(shù)加起來蛔溃,然后再根據(jù)占的比重來決定寬度绰沥,現(xiàn)在很明顯是EditText和Button平分寬度。

這時候如果Button的layout_width改為wrap_content城榛,然后EditText保持1的比比重不變揪利,那么Button就會保持固定大小,然后EditText占據(jù)剩余的寬度空間狠持。

3.3.2 相對布局

相對布局RelativeLayout疟位,相對隨意,它可以通過相對定位的方式讓控件出現(xiàn)在布局的任何位置喘垂。因此甜刻,屬性非常多绍撞,不過這些屬性都是有規(guī)律可循。

如案例:5個按鈕得院,分別在左上傻铣,右上,中間祥绞,左下非洲,右下。

<RelativeLayout xmlns:android:”http://schemas.android.com/apk/res/android

android:layout_width=“match_parent”

android:layout_height=“match_parent” >

<Button

android:id=“@+id/button1”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_alignParentLeft=“true”

android:layout_alignParentTop=“true”

android:text=“Button 1” />

<Button

android:id=“@+id/button2”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_alignParentRight=“true”

android:layout_alignParentTop=“true”

android:text=“Button 2” />

<Button

android:id=“@+id/button3”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_centerInParent=“true”

android:text=“Button 3” />

<Button

android:id=“@+id/button4”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_alignParentLeft=“true”

android:layout_alignParentBottom=“true”

android:text=“Button 4” />

<Button

android:id=“@+id/button5”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_alignParentRight=“true”

android:layout_alignParentBottom=“true”

android:text=“Button 5” />

</RelativeLayout>

上面是相對于父控件進行定位的蜕径。

如果使用相對于其他控件的相對布局两踏,用下面的方式

android:layout_above=“@id/button3” 上方

android:layout_toRightOf=“@id/button3” 右側

android:layout_toLeftOf=“@id/button3” 左側

android:layout_below=“@id/button3” 下側

android:layout_alignLeft 讓一個控件的左邊緣和另一個控件的左邊緣對齊

android:layout_alignRight 讓一個控件的右邊緣和另一個控件的右邊緣對齊

android:layout_alignTop 上邊緣對齊

android:layout_alignBottom 下邊緣對齊

3.3.3 幀布局

幀布局FrameLayout,相對其他兩種布局簡單多了兜喻,應用場景也少了很多梦染。

這種布局沒有方便的定位方式,所有的控件都會默認擺放在布局的左上角

<FrameLayout xmlns:android=“http://schemas.android.com/apk/res/android

android:layout_width=“match_parent”

android:layout_height=“match_parent” >

<TextView

android:id=“@+id/text_view”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“This is TextView” />

<ImageView

android:id=“@+id/image_view”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:src=“@mipmap/ic_launcher” />

</FrameLayout>

FrameLayout還可以使用layout_gravity屬性

Android:layout_gravity=“l(fā)eft”

Android:layout_gravity=“right”

由于FrameLayout的定位方式的缺失朴皆,導致它的使用場景比較少帕识。

3.3.4 百分比布局

前面3種布局都是從android 1.0版本就開始支持了,一直沿用到現(xiàn)在遂铡。這種布局中肮疗,我們可以不再使用wrap_content、match_parent等方式來指定控件的大小忧便,而是允許直接指定控件在布局中所占的百分比族吻。

百分比布局只為FrameLayout和RelativeLayout進行了功能拓展,提供了PercentFrameLayout和PercentRelativeLayout這兩種全新的布局珠增。

怎樣做到新增的百分比布局在所有的android版本中都能使用呢。為此砍艾,android團隊將百分比布局定義在support庫當中蒂教。我們只需要在項目中build.gradle中添加百分比布局庫的以來,就能保證百分比布局在android所有系統(tǒng)版本上的兼容性了脆荷。

修改app/build.gradle文件中

Dependencies {

implementation ‘com.android.support:percent:28.0.0’

}

如果發(fā)現(xiàn)28.0.0版本不夠新凝垛,系統(tǒng)會提示你修改。修改完之后還需要重新build一下蜓谋。(點擊Sync Now)

現(xiàn)在使用PercentFrameLayout

app:layout_widthPercent=“50%” // 將寬度指定為布局的50%

app:layout_heightPercent=“50%” // 將高度指定為布局的50%

PercentFrameLayout還是會繼承FrameLayout的特性梦皮,即所有的控件默認都擺放在布局的左上角,為了讓四個按鈕不會重疊桃焕,還是接住了layout_gravity分別將4個按鈕放置在左上剑肯、右上、左下观堂、右下四個位置让网。android:layout_gravity="left|bottom"

PercentRelativeLayout的用法類似呀忧,也是繼承了RelativeLayout的所有屬性,然后再結合app:layout_widthPercent和app:layout_heightPercent來按百分比指定控件的寬高

3.4 創(chuàng)建自定義控件

所有控件都是繼承自View溃睹,所有的布局都是直接或者間接繼承自ViewGroup的而账。

創(chuàng)建一個新的應用UICustomViews

單獨創(chuàng)建一個title.xml,用于設定類似iOS的頂部導航欄因篇,然后在activity_main.xml中添加<include layout=“@layout/title” />

android:background=“@drawable/title_bg.png” // 用于指定背景圖片

Android:layout_marginLeft,layout_marginRight,layout_mariginTop,layout_marginBottom用于指定上下左右偏移的距離

android:background可以指定一個背景圖片.

android:layout_margin屬性可以指定控件在上下左右方向上偏移的距離泞辐,當然也可以使用

android:layout_marginLeft或者android:layout_marginTop等屬性來單獨指定控件在某個方向上便宜的距離。

接下來修改activity_main.xml

加上<include layout=“@layout/title” />

最后在MainActivity中將系統(tǒng)自帶的標題欄隱藏掉

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main(;

ActionBar actionBar = getSupportActionBar();

if (actionBar != null) {

actionBar.hide();

}

}

}

ActionBar就是我們的導航欄

隱藏標題欄之后竞滓,我們自己寫的tilte.xml就生效了咐吼。以后只要一句include就可以包含進來了。

3.4.2 創(chuàng)建自定義控件

新建TitleLayout繼承自LinearLayout虽界,讓它成為我們自定義的標題欄控件

public class TitleLayout extends LinearLayout {

public TitleLayout(Context context, AttributeSet attrs) {

super(context, attrs);

LayoutInflater.from(context).inflate(R.layout.title, this);

}

}

在布局中引入TitleLayout控件就會調用這個構造函數(shù)汽烦,然后在構造函數(shù)中需要對標題欄布局進行動態(tài)加載,這就要借助LayoutInflater來實現(xiàn)莉御。通過LayoutInflater的from()方法可以構建出一個ayoutInflater對象撇吞,然后調用inflate()方法就可以動態(tài)加載一個布局文件,inflate()方法兩個參數(shù)礁叔,第一個是加載的布局文件的id牍颈,這里傳入R.layout.title,第二個參數(shù)是給加載好的布局再添加一個父布局琅关,這里我們想要指定為TitleLayout煮岁,于是直接傳入this。

現(xiàn)在這個自定義控件創(chuàng)建好了涣易,然后我們需要在布局文件中添加這個自定義控件画机,修改activity_main.xml中的代碼,如下所示:

<LinearLayout xmlns:android=“http”//schemas.android.com/apk/res/android

android:layout_width=“match_parant”

android:layout_height=“match_parent” >

// 包名在這里是不可以省略的

<com.example.uicustomviews.TitleLayout

android:layout_width=“match_parent”

android:layout_height=“wrap_content” />

</LinearLayout>

下面我們嘗試為標題欄中的按鈕注冊點擊事件新症,修改TitleLayout中的代碼步氏,如下

public class TitleLayout extends LinearLayout {

public TitleLayout(Context context, AttributeSet attrs) {

super(context, attrs);

LayoutInflater.from(context).inflate(R.layout.title, this);

Button titleBack = (Button) findViewById(R.id.title_back);

Button titleEdit = (Button) findViewById(R.id.title_edit);

titleBack.setOnclickListener(new OnClickListener() {

@Override

public void onClick(View v) {

((Activity) getContext()).finish();

}

});

titleEdit.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

Toast.makeText(getContext(), “You click Edit button”, Toast.LENGTH_SHORT).show();

}

});

}

}

首先還是通過findViewById()方法得到按鈕的實例,然后分別調用setOnclickListenner()方法給兩個按鈕注冊了點擊事件徒爹,

當點擊返回按鈕的時候銷毀當前的活動

當點擊編輯按鈕的時候彈出一段文本荚醒。

3.5 最常用和最難用的空控件 —》 ListView(相當于iOS中的UITableView)

3.5.1 LIstView的簡單用法

新建一個項目ListViewTest項目,然后修改activity_main.xml中的代碼

<LinearLayout xmlns:android=android=“http://schemas.android.com/apk/res/android

android:layout_width=“match_parent”

android:layout_height=“match_parent” >

<ListView android:id=“@+id/list_view”

android:layout_width=“match_parent”

android:layout_height=“match_parent” />

</LinearLayout>

接下來修改MainActivity的代碼

public class MainActivity extends AppCompatActivity {

private String[] data = {

“Apple”,”Banana”,”O(jiān)range”,”watermelon”,”Pear”,”Grape”,”Pineapple”,”Strawberry”,”Cherry”,”Mango”,

“Apple”,”Banana”,”O(jiān)range”,”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);

}

}

我們需要通過借助適配器來把數(shù)據(jù)傳遞給ListView隆嗅,Android中提供了很多適配器的實現(xiàn)類界阁,其中我認為最好用的就是ArrayAdapter。它可以通過泛型來指定要適配的數(shù)據(jù)類型胖喳,然后在構造函數(shù)中把要適配的數(shù)據(jù)傳入泡躯。ArrayAdater有多個構造函數(shù)的重載,你應該根據(jù)實際情況選擇最合適的一種。上面在ArrayAdapter的構造函數(shù)中依次穿日當前上下文精续、ListView子項布局的id坝锰,以及要適配的數(shù)據(jù)。注意重付,我們使用了android.R.layout.simple_list_item_1作為子項布局的id顷级,這是一個Android內置的布局文件,里面只有一個TextView确垫,可用于簡單的顯示一段文本弓颈。

3.5.2 定制ListView的界面

只顯示一段文本的LIstView太單調了,現(xiàn)在我們來對ListView的界面進行定制删掀。

首先我們需要準備好一組圖片翔冀,分別對應提供的每一種水果。

接著定義一個實體類披泪,作為ListView適配器的適配類型纤子。新建Fruit類,代碼如下:

public class Fruit {

private String name;

private int imageId;

public Fruit(String name, int imageId) {

this.name = name; // 水果名

this.imageId = imageId; // 水果資源id

}

public String getName() {

return name;

}

public int getImageId() {

return imageId;

}

}

然后我們?yōu)長istVie的子項指定一個我們自定義的布局款票,在layout目錄下新建fruit_item.xml控硼,代碼如下

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android

android:layout_width=“match_parent”

android:layout_height=“match_parent” >

<ImageView

android:id=“@+id/fruite_image”

android:layout_width=“wrap_content”

android:laytout_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=“10dp” />

</LinearLayout>

上面的代碼中,我們定義了一個ImageView用于顯示水果的圖片艾少,又定義了一個TextView用于顯示水果的名稱卡乾,并讓TextView在垂直方向上居中顯示。

接下來我們創(chuàng)建一個自定義的適配器缚够,這個適配器繼承自ArrayAdapter幔妨,并將泛型指定為Fruit類,新建類FruitAdapter:

public class FruitAdapter extends ArrayAdapter<Fruit> {

private int resourceId; // 圖片資源id

public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {

super(context, textViewResourceId, objects);

resourceId = textViewResoureceId;

}

@Override

public View getView(int positon, VIew convertView, ViewGroup parent) {

Fruit fruit = getItem(position); // 獲取當前項的Fruit實例

View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, flase);

ImageView fruitImage = (ImageView) findViewById(R.id.fruit_image);

TextView fruitName = (TextView) findViewById(R.id.fruit_name);

fruitImage.setImageResource(fruit.getImageId());

fruitName.setText(fruit.getName());

return View;

}

}

FruitAdapter重寫了父類的一組構造函數(shù)谍椅,用于將上下文误堡,ListView子項布局的id和數(shù)據(jù)傳遞過去。另外又重寫了getVIew()方法雏吭,這個方法在每一個子項被滾動到屏幕內的時候會被調動埂伦。在getView()方法中,首先通過getItem()方法得到當前項的Fruit實例思恐,然后使用LayoutInflater來為這個子項加載我們傳入的布局。

這里的Layouinflater的inflater()方法接收3個參數(shù)膊毁,前面兩個參數(shù)我們都知道是什么意思了胀莹,第三個參數(shù)指定成flase,表示只讓我們在父布局中聲明的layout屬性生效婚温,但不為這個View添加父布局描焰,因為一旦View有了父布局之后,它就不能再天機到ListView中了。(現(xiàn)在不太明白沒關系荆秦,這其實是ListView中的標準寫法)

最后我們修改MainActivity中的代碼篱竭,

public class MainActivity extends AppCompatActivity {

private List<Fruit> fruitList = new ArrayList<>(); // 水果數(shù)據(jù)

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState) ;

setContentView(R.layout.activity_main);

initFruits(); // 初始化水果數(shù)據(jù)

FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);

ListView listView = (ListView) findViewById(R.id.list_view);

listView.setAdapter(adapter);

}

private void initFruit() {

for (int i = 0; i < 2; i++) {

Fruit apple = new Fruit(“Apple”, R.drawable.apple_pic);

fruitList.add(apple);

Fruit banana = new Fruit(“Banana”, R.drawable.apple_pic);

fruitList.add(banana);

}

}

}

只要修改fruit_item.xml中的內容,就可以定制出各種復雜的界面了步绸。

3.5.3 提升ListView的運行效率

目前我們的LIstView的運行效率是很低的掺逼,因為在FruitAdapter的getView()方法中,每次都將布局重新加載了一遍瓤介,當ListView快速滾動的時候吕喘,這就會成為性能的瓶頸。

仔細觀察刑桑,getView()方法還有一個converView的參數(shù)氯质,這個參數(shù)用于將之前加載好的布局進行緩存,以便之后可以進行重用祠斧。修改FruitAdapter中的代碼闻察,如下

public class FruitAdapter extends ArrayAdapter<Fruit> {

@Override

public View getView(int positon, View convertView, VIewGroup parent) {

Fruit fruit = getItem(position);

View view;

if (convertView == null) {

view = LayoutFlater.from(getContext()).inflate(resourceId, parent, false);

} else {

view = convertView;

}

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;

}

}

雖然現(xiàn)在已經不會重復加載布局,但是每次在getView()的時候琢锋,還是會調用View的findViewById()方法來獲取一次控件的實例辕漂。繼續(xù)優(yōu)化,使用ViewHolder來使得不會重復創(chuàng)建吩蔑。

public View getView(int position, View convertView, ViewGroup parent) {

Fruit fruit = getItem(position);

View view;

ViewHolder viewHolder;

if (convertVIew == null) {

view = LayoutFlater.from(getContext()).inflate(resourceId, parent, false);

viewHolder = new ViewHolder();

veiwHolder.fruitImage = (ImageView)view.findViewById(R.id.fruit_image);

viewHolder.fruitName = (TextView)view.findViewById(R.id.fruit_name);

view.setTag(viewHolder); // 將ViewHolder存儲在View中钮热。

} else {

view = convertView;

viewHolder = (ViewHolder)view.getTag();

}

viewHolder.fruitImage.setImageResource(fruit.getImageId());

viewHolder.fruitNma.setText(fruit.getName());

return view;

}

class ViewHolder {

ImageView fruitImage;

TextView fruitName;

}

3.5.4 ListView的點擊事件

修改MainActivity中的代碼

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

@Override

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

Fruit fruit = fruitList.get(position);

Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();

}

});

3.6 更強大的滾動控件 — RecyclerView

ListView雖然很好用,擴展性不好烛芬,只能實現(xiàn)縱向的滾動效果隧期,而且如果我們不使用一些技巧來提升它的運行效率,那么LIstView的性能就會非常差赘娄。

RecyclerView是增強版的ListView仆潮,可以輕松實現(xiàn)ListView的效果,還優(yōu)化了ListView的各種不足之處遣臼。目前Android更推薦使用RecyclerView性置。

新建一個RecyclerViewTest的項目

android團隊將RecyclerView定義在support庫當中,想要使用RecyclerVIew這個控件揍堰,需要再項目中添加相應的依賴庫才行鹏浅。

打開app/build.gradle文件,在dependencies中添加如下內容

dependencies {

compile fileTree(dir: ‘libs’, include: [‘*.jar’])

compile ‘com.android.support:appcompat-v7:24.2.1’

compile ‘com.android.support:recyclerview-v7:24.2.1’

testCompile ‘junit:junit:4.12’

}

Warming:

新版本配置如下:recyclerview的版本應該和appcompat的版本保持一致屏歹。

dependencies {

implementation fileTree(dir: 'libs', include: ['.jar'*])

implementation 'androidx.appcompat:appcompat:1.0.2'

implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

implementation 'androidx.recyclerview:recyclerview:1.1.0-beta02'

testImplementation 'junit:junit:4.12'

androidTestImplementation 'androidx.test:runner:1.2.0'

androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

}

添加完之后點擊Sync Now來進行同步一下隐砸,然后修改activity_main.xml中的代碼,

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android

android:layout_width=“match_parent”

android:layout_height=“match_parent” >

// 由于RecyclerView并不是在系統(tǒng)SDK中蝙眶,所以需要把完整的包名路徑寫出來

<android.support.v7.widget.RecyclerView

android:id=“@+id/recycler_view”

android:layout_width=“match_parent”

android:layout_height=“match_parent” />

</LinearLayout>

為了使得RecyclerView達到和ListView相同的效果季希。我們需要把圖片。Fruit類,fruit_item.xml復制過來式塌。免得再寫一遍博敬。

新建一個新的FruitAdapter,繼承自RecyclerVIew.Adapter峰尝,并將泛型指定為FruitAdapter.ViewHolder

偏窝。。境析。

3.6.2 RecyclerView實現(xiàn)橫向滾動和瀑布流布局

首先對fruit_item.xml進行修改囚枪,因為這個布局目前是水平排列的。

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android

android:orientation=“vertical”

android:layout_width=“100dp”

android:layout_height=“wrap_content” >

// 垂直方向排列

<ImageView android:id=“@+id/fruit_image”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_gravity=“center_horizontal” />

// 垂直方向排列

<TextView android:id=“@+id/fruit_name”

android:layout_width=“wrap_content”

android:layotu_height=“wrap_content”

android:layout_gravity=“center_hotizontal”

android:layout_marginTop=“10dp” />

</LinearLayout>

現(xiàn)在修改MainActivity的代碼

public class MainActivity extends AppCompatActivity {

private List<Fruit> fruitList = new ArrayList<>();

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(saveInstanceState);

setContentView(R.layout.activity_main);

initFruit();

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

LinearLayoutManager layoutManager = new LinearLayoutManager(this);

layoutManager.setOrientaion(LinearLayoutManager.HORIZONTAL);

recyclerView.setLayoutManager(layoutManager);

FruitAdapter adapter = new FruitAdapter(fruitList);

recyclerView.setAdapter(adapter);

}

}

調用LinearLayoutManger的setOrientation()方法來設置布局的排列方向劳淆,默認是縱向排列的链沼,我們傳入LinearLayoutManger.HORIZONTAL表示讓布局橫行排列。

ListView的布局排列是由自身去管理的沛鸵,而RecyclerView則將這個工作交給了LayoutManger括勺,LayoutManage中制定了一套殼拓展的布局排列接口,子類只要按照接口的規(guī)范來實現(xiàn)曲掰,就能制定出各種不同排列方式的布局了疾捍。

處理LinearLayoutManger,RecyclerView還給我們提供了GridLayoutManger和StaggeredGridLayoutManager這兩種內置的布局排列方式栏妖。GridLayoutManaget可以用于實現(xiàn)網格布局乱豆,StaggeredGridLayoutManager可以用于實現(xiàn)瀑布流布局。

現(xiàn)在我們來實現(xiàn)更加炫酷的瀑布流布局(相當于iOS中的UICollectionVeiw)

繼續(xù)修改fruit_item.xml

<LinearLayout xmlns:android=“http://schemas.adnroid.com/apk/res/android

adnroid:orientation=“vertical”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_margin=“5dp” >

<ImageView

android:id=“@+id/fruit_image”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_gravity=“center_horizontal” />

<TextView

android:id=“@+id/fruit_name”

android:layout_width=“wrap_contetn”

android:layout_height=“wrap_contetn”

android:layout_gravity=“Left”

android:layout_marginTop=“10dp” />

</LinearLayout>

這里做了一些小調整吊趾,首先將LinearLayout的寬度由100dp修改為match_parent宛裕,因為瀑布流布局的寬度應該是根據(jù)布局的列數(shù)來自動適配的。而不是一個固定的值论泛。另外我們使用了layout_maring屬性來讓子項之間互留一點間距揩尸。還有將TextView的對齊屬性改成了居左對齊。

接著修改MainAcitivity代碼:

public class MainActivity extends AppCompatActivity {

private List<Fruit> fruitList = new ArrayList<>();

@Override

protected void onCreate(Bundel savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main):

initFruits();

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

// 構造函數(shù)接收兩個參數(shù)屁奏,3表示布局分為3列岩榆,第二個參數(shù)用于指定布局的排列方式。

StaggerdGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTCAL);

recyclerView.setLayoutManager(layoutManager);

FruitAdapter adapter = new FruitAdapter(fruitList);

recyclerView.setAdapter(adapter);

}

private void initFruit() {

for (int i = 0; i < 2; i++) {

Fruit apple = new Fruit(getRandomLengthName(“Apple”, R.drawable.apple_pic);

fruitList.add(apple);

}

}

private Strring getRandomLengthName(String name) {

Random random = new Random();

// 創(chuàng)建一個1-20的隨機數(shù)坟瓢,

int length = random.nextInt(20) + 1;

StringBuilder builder = new StringBuilder();

for (int i = 0; i < length; i++ ) {

builder.append(name);

}

// 輸出字符串

return builder.toString();

}

}

3.6.3 RecyclerView的點擊事件

不同于ListView勇边,RecyclerView并沒有提供類似于setOnItemClickListener()這樣的注冊監(jiān)聽器方法。而是需要我們自己給子項具體的view去注冊點擊事件折联,相對于ListView來說粥诫,實現(xiàn)起來要復雜一點。

ListView的點擊事件上的處理并不人性化崭庸,setOnItemClickListener()方法注冊的是子項的點擊事件,但如果我們想點擊的是子項里面具體的某一個按鈕呢,ListView也能實現(xiàn)怕享,但是實現(xiàn)起來就相對比較麻煩了执赡。為此,RecyclerView干脆直接摒棄了子項點擊事件的監(jiān)聽器函筋,所有的點擊事件都由具體的View去注冊沙合,就再沒有這個困擾了。

修改FruitAdapter中的代碼:

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {

private List<Fruit> mFruitList;

static class ViewHolder extends RecyclerView.ViewHolder {

View fruitView;

ImageView fruitImage;

TextView fruitName;

public ViewHolder(View view) {

super(view);

fruitView = view;

fruitImage = (ImageView) view.findViewById(R.id.fruit_image);

fruitName = (TextView) view.findViewById(R.id.fruit_name);

}

}

public FruitAdapter(List<Fruit> fruitList) {

mFruitList = fruitList;

}

@Override

public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);

final ViewHolder holder = new ViewHolder(view);

holder.fruitVeiw.setOnClickListener( new View.OnClickListener() {

@Ovrride

public void onClick(View v) {

int position = holder.getAdapterPosition();

Fruit fruit = mFruitList.get(position);

Toast.makeText(v.getContext(), “you clicked view” + fruit.getName(), Toast.LENTH_SHORT()).show();

}

)};

holder.fruitImage.setOnclickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

int position = holder.getAdapterPositon();

Fruit fruit = mFruitList.get(position);

Toast.makeText(v.getContext(), “you clicked view” + fruit.getName(), Toast.LENTH_SHORT).show();

}

)};

return holder;

}

}

RecyclerView的強大之處在于跌帐,它可以輕松實現(xiàn)子項中任意控件或布局的點擊事件首懈。

3.7 編寫界面的最佳實踐。

現(xiàn)在我們結合之前所有的知識谨敛,來做衣蛾較為復雜且沒管的聊天界面究履。創(chuàng)建一個UIBestPractice項目。

具體代碼看Android Studio里面的代碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末脸狸,一起剝皮案震驚了整個濱河市最仑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炊甲,老刑警劉巖泥彤,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卿啡,居然都是意外死亡亡呵,警方通過查閱死者的電腦和手機优炬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人誉尖,你說我怎么就攤上這事♀擅耍” “怎么了缘圈?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長野崇。 經常有香客問我称开,道長,這世上最難降的妖魔是什么乓梨? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任鳖轰,我火速辦了婚禮,結果婚禮上扶镀,老公的妹妹穿的比我還像新娘蕴侣。我一直安慰自己,他們只是感情好臭觉,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布昆雀。 她就那樣靜靜地躺著辱志,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狞膘。 梳的紋絲不亂的頭發(fā)上揩懒,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機與錄音挽封,去河邊找鬼已球。 笑死,一個胖子當著我的面吹牛辅愿,可吹牛的內容都是我干的智亮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼点待,長吁一口氣:“原來是場噩夢啊……” “哼阔蛉!你這毒婦竟也來了?” 一聲冷哼從身側響起亦鳞,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤馍忽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后燕差,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遭笋,經...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年徒探,在試婚紗的時候發(fā)現(xiàn)自己被綠了瓦呼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡测暗,死狀恐怖央串,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情碗啄,我是刑警寧澤质和,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站稚字,受9級特大地震影響饲宿,放射性物質發(fā)生泄漏。R本人自食惡果不足惜胆描,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一瘫想、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昌讲,春花似錦国夜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筹裕。三九已至,卻和暖如春礼搁,著一層夾襖步出監(jiān)牢的瞬間饶碘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工馒吴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瑟曲。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓饮戳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親洞拨。 傳聞我的和親對象是個殘疾皇子扯罐,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內容