本章的主要的知識(shí)點(diǎn):
- 卡片式布局
- CardView
- AppBarLayout
- 下拉刷新
- 可折疊式標(biāo)題欄
- CollapsingToolbarLayout
- 充分利用系統(tǒng)狀態(tài)欄空間
12.5 卡片式布局
卡片式布局也是Material Design中提出的一個(gè)新的概念,它可以讓頁(yè)面中的元素看起來(lái)就像在卡片中一樣邪狞,并且還能擁有圓角和投影。
12.5.1 CardView
CardView
是用于實(shí)現(xiàn)卡片式布局效果的重要控件雪猪,有appcompat-v7
庫(kù)提供乏奥,實(shí)際上,CardView
也是一個(gè)FrameLayout
,只是額外提供了圓角和陰影等效果贾铝,看上去會(huì)有立體的感覺(jué)。
<android.support.v7.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="4dp"
app:elevation="5dp">
<TextView
android:id="@+id/info_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v7.widget.CardView>
這里定義了一個(gè)CardView
布局,我們可以通過(guò)app:cardCornerRadius
屬性指定卡片圓角的弧度垢揩,數(shù)值越大玖绿,圓角的弧度也越大。另外還可以通過(guò)app:elevation
屬性指定卡片的高度叁巨,高度值越大斑匪,投影范圍也越大,但是投影效果越淡锋勺,高度值越小蚀瘸,投影范圍也越小揪胃,但是投影效果越濃响牛。這一點(diǎn)和FloatingActionButton
是一致的碎乃。
添加依賴
compile 'com.android.support:cardview-v7:25.1.1'
compile 'com.android.support:recyclerview-v7:25.1.1'
compile 'com.github.bumptech.glide:glide:3.7.0'
這里添加了一個(gè)Glide
庫(kù)的依賴族阅。Glide
是一個(gè)超級(jí)強(qiáng)大的圖片加載庫(kù)当宴,它不僅可以用于加載本地圖片铭若,還可以加載網(wǎng)絡(luò)圖片冰沙,GIF
圖片哑芹,甚至是本地視頻布近。最重要的是垫释,Glide
的用法非常簡(jiǎn)單,只需一行代碼就能輕松實(shí)現(xiàn)復(fù)雜的圖片加載功能撑瞧。
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/done"
app:elevation="8dp"/>
</android.support.design.widget.CoordinatorLayout>
``
</android.support.v4.widget.DrawerLayout>
這里我們?cè)?code>CoordinatorLayout中添加了一個(gè)RecyclerView
,給它指定了一個(gè)id
棵譬,然后將寬度和高度都設(shè)置為match_parent
,這樣RecyclerView
也就占滿了整個(gè)布局的空間。
定義一個(gè)實(shí)體類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;
}
}
Fruit
類中只有兩個(gè)字段预伺,name
表示水果的名字订咸,imageId
表示水果對(duì)應(yīng)圖片的資源id
。
新建fruit_item.xml
<android.support.v7.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="4dp"
android:layout_margin="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/fruit_image"
android:layout_width="match_parent"
android:layout_height="100dp"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="5dp"
android:textSize="16sp"/>
</LinearLayout>
</android.support.v7.widget.CardView>
這里使用了CardView
來(lái)作為子項(xiàng)的最外層布局酬诀,從而使得RecyclerView
中的每個(gè)元素都是在卡片當(dāng)中的脏嚷。CardView
由于是一個(gè)FrameLayout
,因此它沒(méi)有什么方便的定位方式瞒御,這里我們只好在CardView
中再嵌套一個(gè)LinearLayout
父叙,然后在LinearLayout
中放置具體的內(nèi)容。
注意在ImageView
中我們使用了一個(gè)scaleType
屬性肴裙,這個(gè)屬性可以指定圖片的縮放模式趾唱。由于各張水果圖片的長(zhǎng)寬比例可能不一致,為了讓所有的圖片都能填充滿整個(gè)ImageView
蜻懦,這里使用了centerCrop
模式甜癞,它可以讓圖片保持原有比例填充滿ImageView
,并將超出屏幕的部分裁減掉宛乃。
新建FruitAdapter
類悠咱,讓這個(gè)類繼承自RecyclerView.Adapter
,并將泛型指定為FruitAdapter.ViewHolder
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>
{
private Context mContext;
private List<Fruit> mFruitList;
public FruitAdapter(Context context, List<Fruit> mFruitList)
{
this.mContext = context;
this.mFruitList = mFruitList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
if (mContext != null)
{
mContext = parent.getContext();
}
View view = LayoutInflater.from(mContext).inflate(R.layout.fruit_item,parent,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(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
{
CardView cardView;
ImageView fruitImage;
TextView fruitName;
public ViewHolder(View itemView)
{
super(itemView);
cardView = (CardView) itemView;
fruitImage = (ImageView) itemView.findViewById(R.id.fruit_image);
fruitName = (TextView) itemView.findViewById(R.id.fruit_name);
}
}
}
首先調(diào)用Glide.with()
方法并傳入一個(gè)Context
蒸辆,Activity
或Fragment
參數(shù),然后調(diào)用load()
方法去加載圖片乔煞,可以是一個(gè)URL
地址吁朦,也可以是一個(gè)本地路徑,或則是一個(gè)資源id
渡贾,最后調(diào)用into()
方法將圖片設(shè)置到具體一個(gè)ImageView
中就可以了逗宜。
這次我從網(wǎng)上找的這些水果圖片像素都非常高,如果不進(jìn)行壓縮就直接展示的話空骚,很容易就會(huì)引起內(nèi)存溢出纺讲。而使用Glide
就完全不需要擔(dān)心這回事,因?yàn)?code>Glide在內(nèi)部做了許多非常復(fù)雜的邏輯操作囤屹,其中就包括了圖片壓縮熬甚,我們只需要安心按照Glide
的標(biāo)準(zhǔn)用法去加載圖片就可以了。
public class MainActivity extends AppCompatActivity {
private Fruit[] fruits = {
new Fruit("Apple",R.drawable.apple),
new Fruit("Banana",R.drawable.banana),
new Fruit("Orange",R.drawable.orange),
new Fruit("Watermelon",R.drawable.watermelon),
new Fruit("Pear",R.drawable.pear),
new Fruit("Grape",R.drawable.grape),
new Fruit("Pineapple",R.drawable.pineapple),
new Fruit("Strawberry",R.drawable.strawberry),
new Fruit("Cherry",R.drawable.cherry),
new Fruit("Mango",R.drawable.mango)};
private List<Fruit> fruitList = new ArrayList<>();
private FruitAdapter adapter;
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
``
initFruits();
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
GridLayoutManager layoutManager = new GridLayoutManager(this,2);
recyclerView.setLayoutManager(layoutManager);
adapter = new FruitAdapter(this,fruitList);
recyclerView.setAdapter(adapter);
}
public void initFruits()
{
fruitList.clear();
for (int i = 0; i < 50; i++)
{
Random random = new Random();
int index = random.nextInt(fruits.length);
fruitList.add(fruits[index]);
}
}
}
在MainActivity
中我們首先定義了一個(gè)數(shù)組肋坚,數(shù)組里面存放了很多個(gè)Fruit
的實(shí)例乡括,每個(gè)實(shí)例都代表著一種水果。然后在initFruit()
方法中智厌,先是清空一下fruitList
中的數(shù)據(jù)诲泌,接著使用一個(gè)隨機(jī)函數(shù),從剛才定義的Fruit
數(shù)組中隨機(jī)挑選一個(gè)水果放入fruitList
當(dāng)中铣鹏,這樣每次打開程序看到的水果數(shù)據(jù)都是不同的敷扫。
仔細(xì)觀察可以看到Toolbar
被RecyclerView
擋住了,這就需要借助AppBarLayout
了
12.5.2 AppBarLayout
由于RecyclerView
和Toolbar
都是放置在CoordinatorLayout
中的诚卸,CoordinatorLayout
就是一個(gè)加強(qiáng)版的FrameLayout
葵第。那么FrameLayout
中的所有控件在不進(jìn)行明確定位的情況下,默認(rèn)都會(huì)排放在左上角合溺,從而也就產(chǎn)生了遮擋的現(xiàn)象卒密。
傳統(tǒng)情況下使用偏移是唯一的解決辦法,則讓RecyclerView
向下偏移一個(gè)Toolbar
的高度棠赛,從而保證到不會(huì)遮擋到Toolbar
哮奇。
這里我準(zhǔn)備使用Design Support
庫(kù)中提供的另外一個(gè)工具---AppBarLayout
。AppBarLayout實(shí)際上是一個(gè)垂直方向上的LinearLayout恭朗,它在內(nèi)部做了很多滾動(dòng)事件的封裝,并應(yīng)用了一些Material Design
的設(shè)計(jì)理念依疼。
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v7.widget.RecyclerView>
``
</android.support.design.widget.CoordinatorLayout>
我們首先定義了一個(gè)AppBarLayout
痰腮,并將Toolbar
放置在了AppBarLayout
里面,然后在RecyclerView
中使用app:layout_behavior
屬性指定了一個(gè)布局行為律罢,其中appbar_scrolling_view_behavior
這個(gè)字符串也是由Design Support
庫(kù)提供的膀值。
事實(shí)上棍丐,當(dāng)RecyclerView
滾動(dòng)的時(shí)候就已經(jīng)將滾動(dòng)事件都通知給AppStoreBarLayout
了只是我們還沒(méi)進(jìn)行處理而已。
當(dāng)AppBarLayout
接收到滾動(dòng)事件的時(shí)候沧踏,它內(nèi)部的子控件是可以指定如何去影響這些事件的歌逢,通過(guò)app:layout_scrollFlags
屬性就能實(shí)現(xiàn)。
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways|snap">
</android.support.v7.widget.Toolbar>
這里在Toolbar
中添加了一個(gè)app:layout_scrollFlags
屬性翘狱,并將這個(gè)屬性的值指定成了scroll|enterAlways|snap
秘案。其中,scroll
表示當(dāng)RecyclerView
向上滾動(dòng)的時(shí)候潦匈,Toolbar
會(huì)跟著一起向上滾動(dòng)并實(shí)現(xiàn)隱藏阱高;enterAlways
表示當(dāng)RecyclerView
向下滾動(dòng)的時(shí)候,Toolbar
會(huì)跟著一起向下滾動(dòng)并重新現(xiàn)實(shí)茬缩,snap
表示Toolbar
還沒(méi)有完全隱藏或顯示的時(shí)候赤惊,會(huì)根據(jù)當(dāng)前滾動(dòng)的距離,自動(dòng)選擇是隱藏還是顯示凰锡。
12.6 下拉刷新
谷歌為了讓Android
的下拉刷新風(fēng)格能有一個(gè)統(tǒng)一的標(biāo)準(zhǔn)未舟,于是在Material Design
中制定了一個(gè)官方的設(shè)計(jì)規(guī)范。
SwipeRefreshLayout
就是用于實(shí)現(xiàn)下拉刷新功能的核心類掂为,它是由support-v4
庫(kù)提供的裕膀。我們把想要實(shí)現(xiàn)下拉刷新功能的控件放置到SwipeRefreshLayout
中,就可以迅速讓這個(gè)控件支持下拉刷新菩掏。
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
``
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
``
我們?cè)?code>RecyclerView的外面又嵌套了一層SwipeRefreshLayout
魂角,這樣RecyclerView
就自動(dòng)擁有下拉刷新功能了。另外需要注意智绸,由于RecyclerView
現(xiàn)在變成了SwipeRefreshLayout
的子控件野揪,因此之前使用app:layout_behavior
聲明的布局行為現(xiàn)在也要移到SwipeRefreshLayout
中才行。
雖然RecyclerView
已經(jīng)支持下拉刷新功能了瞧栗,但是我們還要在代碼中處理具體的刷新邏輯才行斯稳。
public class MainActivity extends AppCompatActivity
{
······
private SwipeRefreshLayout swipeRefreshLayout;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
{
@Override
public void onRefresh()
{
refreshFruits();
}
});
}
······
private void refreshFruits()
{
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
runOnUiThread(new Runnable()
{
@Override
public void run()
{
initFruits();;
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
});
}
}).start();
}
}
首先通過(guò)findViewById()
方法拿到SwipeRefreshLayout
的實(shí)例,然后調(diào)用setColorSchemeResources()
方法來(lái)設(shè)置下拉刷新進(jìn)度條的顏色迹恐,這里我們使用主題中的colorPrimary
作為進(jìn)度條的顏色了挣惰。接著調(diào)用setOnRefreshListener()
方法來(lái)設(shè)置一個(gè)下拉刷新的監(jiān)聽器,當(dāng)觸發(fā)了下拉刷新操作的時(shí)候就會(huì)回調(diào)這個(gè)監(jiān)聽器的onRefresh()
方法殴边,然后我們?cè)谶@里去處理具體的刷新邏輯就行了憎茂。
通常情況下,onRefresh()
方法中應(yīng)該是去網(wǎng)絡(luò)上請(qǐng)求最新的數(shù)據(jù)锤岸,然后再將這些數(shù)據(jù)展示出來(lái)竖幔。refreshFruits()
方法中先是開啟了一個(gè)線程,然后將線程沉睡兩秒鐘是偷。之所以這麼做拳氢,是因?yàn)楸镜厮⑿虏僮魉俣确浅募逞?欤绻粚⒕€程沉睡的話馋评,刷新立刻就結(jié)束了放接,從而看不到刷新的過(guò)程。沉睡結(jié)束之后留特,這里使用了runOnUiThread()
方法將線程切換回主線程纠脾,然后調(diào)用initFruits()
方法重新生成數(shù)據(jù),接著再調(diào)用FruitAdapter
的notifyDataSetChanged()
方法通知數(shù)據(jù)發(fā)生了變化磕秤,最后調(diào)用SwipeRefreshLayout
的setRefershing()
方法并傳入false
乳乌,用于表示刷新事件結(jié)束,并隱藏刷新進(jìn)度條市咆。
12.7 可折疊式標(biāo)題欄
雖說(shuō)我們現(xiàn)在的標(biāo)題欄是使用Toolbar來(lái)編寫的汉操,不過(guò)它看上去和傳統(tǒng)的ActionBar其實(shí)沒(méi)什么兩樣,只不過(guò)可以響應(yīng)RecyclerView的滾動(dòng)事件來(lái)進(jìn)行隱藏和顯示蒙兰。而Material Design中并沒(méi)有限定標(biāo)題欄必須是長(zhǎng)這樣子的磷瘤,事實(shí)上,我們可以根據(jù)自己的喜好隨意定制標(biāo)題欄的樣式搜变。
12.7.1 CollapsingToolbarLayout
CollapsingToolbarLayout是一個(gè)作用于Toolbar基礎(chǔ)之上的布局采缚,它也是由Design Support庫(kù)提供的。CollapsingToolbarLayout可以讓Toolbar的效果變得更加豐富挠他,不僅僅是展示一個(gè)標(biāo)題欄扳抽,而是能夠?qū)崿F(xiàn)非常華麗的效果。
CollapsingToolbarLayout是不能獨(dú)立存在的殖侵,它在設(shè)計(jì)的時(shí)候就被限定只能作為AppBarLayout的直接子布局來(lái)使用贸呢。而AppBarLayout又必須是CoordinatorLayout的子布局。
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="250dp">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="35dp"
app:cardCornerRadius="4dp">
<TextView
android:id="@+id/fruit_content_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"/>
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@drawable/done"
app:layout_anchor="@id/appBar"
app:layout_anchorGravity="bottom|end"/>
</android.support.design.widget.CoordinatorLayout>
1.我們使用了新的布局CollapsingToolbarLayout拢军,android:theme屬性指定了一個(gè)ThemeOverlay.AppCompat.Dark.ActionBar的主題楞陷,app:contentScrim屬性用于指定CollapsingToolbarLayout在趨于折疊狀態(tài)以及折疊之后的背景色,其實(shí)CollapsingToolbarLayout在折疊之后就是一個(gè)普通的Toolbar茉唉,那么背景色肯定應(yīng)該是colorPrimary了固蛾。app:layout_scrollFlags屬性我們也是見過(guò)的,只不過(guò)之前是給Toolbar指定的度陆,現(xiàn)在也移到外面來(lái)了艾凯。其中scroll表示CollapsingToolbarLayout會(huì)隨著水果內(nèi)容詳情的滾動(dòng)一起滾動(dòng),exitUntilCollapsed表示當(dāng)CollapsingToolbarLayout隨著滾動(dòng)完成折疊之后就保留在界面上懂傀,不再移出屏幕趾诗。
2.我們?cè)贑ollapsingToolbarLayout中定義了一個(gè)ImageView和Toolbar。app:layout_collapseMode鸿竖,它用于指定當(dāng)前控件在CollapsingToolbarLayout折疊過(guò)程中的折疊模式沧竟,其中Toolbar指定成pin,表示在折疊過(guò)程中位置始終保持不變,ImageView指定成parallax,表示會(huì)在折疊的過(guò)程中產(chǎn)生一定的錯(cuò)位便宜缚忧,這種模式的視覺(jué)效果會(huì)非常好悟泵。
3.水果內(nèi)容詳情的最外層布局使用了一個(gè)NestedScrollView,注意它和AppBarLayout是平級(jí)的。我們之前學(xué)過(guò)ScrollView的用法闪水,它允許使用滾動(dòng)的方式來(lái)查看屏幕以外的數(shù)據(jù)糕非,而NestedScrollView在此基礎(chǔ)上還增加了嵌套響應(yīng)滾動(dòng)事件的功能。由于CoordinatorLayout本身已經(jīng)可以響應(yīng)滾動(dòng)事件了球榆,因此我們?cè)谒膬?nèi)部就需要使用NestedScrollView或RecyclerView這樣的布局朽肥。另外,這里還通過(guò)app:layout_behavior屬性指定了一個(gè)布局行為持钉,這和之前在RecyclerView中的用法是一模一樣的衡招。
不管是ScrollView還是NestedScrollView,他們的內(nèi)部都只允許存在一個(gè)直接子布局每强。因此始腾,如果我們想要在里面放入很多東西的話,通常都會(huì)先嵌套一個(gè)LinearLayout空执,然后再在LinearLayout中放入具體的內(nèi)容就可以了浪箭。
3.需要注意的是,這里為了讓界面更加美觀辨绊,我在CardView和TextView上都加一些邊距奶栖。其中CardView的marginTop加了35dp的邊距,這是為下面要編寫的東西留出空間门坷。
4.這里加入了一個(gè)FloatingActionButton宣鄙,它和AppBarLayout以及NestedScrollView是平級(jí)的,F(xiàn)loatingActionButton中使用app:layout_anchor屬性指定了一個(gè)錨點(diǎn)拜鹤,我們將錨點(diǎn)設(shè)置為AppBarLayout框冀,這樣懸浮按鈕就會(huì)出現(xiàn)在水果標(biāo)題欄的區(qū)域內(nèi),接著又使用app:layout_anchorGravity屬性將懸浮按鈕定位在標(biāo)題欄區(qū)域的右下角敏簿。
public class FruitActivity extends AppCompatActivity
{
public static final String FRUIT_NAME = "fruit_name";
public static final String FRUIT_IMAGE_ID = "fruit_image_id";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fruit);
//獲取跳轉(zhuǎn)過(guò)來(lái)的數(shù)據(jù)
Intent intent = getIntent();
String fruitName = intent.getStringExtra(FRUIT_NAME);
int fruitImageId = intent.getIntExtra(FRUIT_IMAGE_ID,0);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
ImageView fruitImageView = (ImageView) findViewById(R.id.fruit_image_view);
TextView fruitContentText = (TextView) findViewById(R.id.fruit_content_text);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null)
{
actionBar.setDisplayHomeAsUpEnabled(true);
}
collapsingToolbar.setTitle(fruitName);
Glide.with(this).load(fruitImageId).into(fruitImageView);
String fruitContent = generateFruitContent(fruitName);
fruitContentText.setText(fruitContent);
}
//500個(gè)水果名去
private String generateFruitContent(String fruitName)
{
StringBuilder fruitContent = new StringBuilder();
for (int i = 0; i < 500; i++)
{
fruitContent.append(fruitName);
}
return fruitContent.toString();
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case android.R.id.home:
finish();
break;
}
return super.onOptionsItemSelected(item);
}
}
通過(guò)Intent獲取到傳入的水果名和水果圖片的資源id明也,然后通過(guò)findViewById()方法拿到剛才在布局文件中定義的各個(gè)控件的實(shí)例。接著就是使用了Toolbar的標(biāo)準(zhǔn)用法惯裕,將它作為ActionBar顯示温数,并啟用HomeAsUp按鈕。HomeAsUp按鈕的默認(rèn)圖標(biāo)就是一個(gè)返回箭頭蜻势。
接下來(lái)開始填充界面上的內(nèi)容撑刺,調(diào)用CollapsingToolbarLayout的setTitle()方法將水果名設(shè)置成當(dāng)前界面的標(biāo)題,然后使用Glide加載傳入的水果圖片握玛,并設(shè)置到標(biāo)題欄的ImageView上面够傍。使用了一個(gè)generateFruitContent()方法將水果名循環(huán)拼接500次甫菠,從而生成了一個(gè)比較長(zhǎng)的字符串,將它設(shè)置到了TextView上面冕屯。
我們?cè)趏nOptionsItemSelected()方法中處理了HomeAsUp按鈕的點(diǎn)擊事件寂诱,當(dāng)點(diǎn)擊了這個(gè)按鈕時(shí),就調(diào)用finish()方法關(guān)閉當(dāng)前的活動(dòng)安聘,從而返回上一個(gè)活動(dòng)痰洒。
@Override
public ViewHolder onCreateViewHolder(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 v)
{
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Intent intent = new Intent(mContext,FruitActivity.class);
intent.putExtra(FruitActivity.FRUIT_NAME,fruit.getName());
intent.putExtra(FruitActivity.FRUIT_IMAGE_ID,fruit.getImageId());
mContext.startActivity(intent);
}
});
return holder;
}
這里我們給CardView注冊(cè)了一個(gè)點(diǎn)擊事件監(jiān)聽器,然后在點(diǎn)擊事件中獲取當(dāng)前點(diǎn)擊項(xiàng)的水果名和水果圖片資源id,把他們傳入到Intent中浴韭,最后調(diào)用startActivity()方法啟動(dòng)FruitActivity丘喻。
這個(gè)界面上的內(nèi)容分為3部分,水果標(biāo)題欄念颈,水果內(nèi)容詳情和懸浮按鈕泉粉。Toolbar和水果背景圖完美地融合到了一起,既保證了圖片的展示空間榴芳,又不影響Toolbar的任何功能搀继,那個(gè)向左的箭頭就是用來(lái)返回上一活動(dòng)的。
12.7.2 充分利用系統(tǒng)狀態(tài)欄空間
你會(huì)發(fā)現(xiàn)水果的背景圖片和系統(tǒng)的狀態(tài)欄總有一些不搭的感覺(jué)翠语。
只不過(guò)很可惜的是叽躯,在Android5.0系統(tǒng)之前,我們是無(wú)法對(duì)狀態(tài)欄的背景或顏色進(jìn)行操作的肌括,那個(gè)時(shí)候也還沒(méi)有Material Design的概念点骑。但是Android5.0及之后的系統(tǒng)都是支持這個(gè)功能的,在Android5.0及之后的系統(tǒng)中谍夭,使用背景圖和狀態(tài)欄融合的模式黑滴,在之前的系統(tǒng)中使用普通的模式。
想要讓背景圖能夠和系統(tǒng)狀態(tài)欄融合紧索,需要借助android:fitsSystemWindows這個(gè)屬性來(lái)實(shí)現(xiàn)袁辈。在CoordinatorLayout,AppBarLayout,CollapsingToolbarLayout這種嵌套結(jié)構(gòu)的布局中珠漂,將控件的android:fitsSystemWindows屬性指定成true晚缩,就表示該控件會(huì)出現(xiàn)在系統(tǒng)狀態(tài)欄里。對(duì)應(yīng)到我們的程序媳危,那就是水果標(biāo)題欄中的ImageView應(yīng)該設(shè)置這個(gè)屬性了荞彼。不過(guò)只給ImageView設(shè)置這個(gè)屬性是沒(méi)有用的,我們必須將ImageView布局結(jié)構(gòu)中的所有父布局都設(shè)置上這個(gè)屬性才可以待笑。
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="com.example.materialtest.FruitActivity"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="250dp"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:fitsSystemWindows="true">
<ImageView
android:id="@+id/fruit_image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
android:fitsSystemWindows="true"/>
········
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
·················
</android.support.design.widget.CoordinatorLayout>
但是即使我們將android:fitsSystemWindows屬性都設(shè)置好了還是沒(méi)有用的鸣皂,因?yàn)檫€必須在程序的主題中將狀態(tài)欄顏色指定成透明色才行。指定成透明色的方法很簡(jiǎn)單,在主題中將android:statusBarColor這個(gè)屬性的值指定成@android:color/transparent就可以了寞缝。但問(wèn)題在于癌压,android:statusBarColor這個(gè)屬性是從API21,也就是Android5.0系統(tǒng)開始才有的荆陆,之前的系統(tǒng)無(wú)法指定這個(gè)屬性措拇。那么,系統(tǒng)差異性的功能實(shí)現(xiàn)就要從這里開始了慎宾。
右擊res目錄--->New--->Directory,創(chuàng)建一個(gè)values-v21目錄浅悉,然后右擊values-v21目錄--->New--->Values resource file,創(chuàng)建一個(gè)styles.xml文件趟据。
<resources>
<style name="FruitActivityTheme" parent="AppTheme">
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
這里我們定義了一個(gè)FruitActivityTheme主題,它是專門給FruitActivity使用的术健。FruitActivityTheme的parent主題是AppTheme汹碱,也就是說(shuō),它繼承了AppTheme中的所有特性荞估。然后我們?cè)贔ruitActivityTheme中將狀態(tài)欄中的顏色指定成透明色咳促,由于values-v21目錄是只有Android5.0及以上的系統(tǒng)才回去讀取的,因此這么生命是沒(méi)有問(wèn)題的勘伺。
但是Android5.0之前的系統(tǒng)卻無(wú)法識(shí)別FruitActivityTheme這個(gè)主題跪腹,因此我們還需要對(duì)values/styles.xml文件進(jìn)行修改。
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="FruitActivityTheme" parent="AppTheme">
</style>
</resources>
可以看到飞醉,這里也定義了一個(gè)FruitActivityTheme主題冲茸,并且parent主題也是AppTheme,但是它的內(nèi)部是空的缅帘。因?yàn)锳ndroid5.0之前的系統(tǒng)無(wú)法指定狀態(tài)欄的顏色轴术,因此這里什么都不用做就可以了。
<activity
android:name=".FruitActivity"
android:theme="@style/FruitActivityTheme">
</activity>
這里使用android:theme屬性單獨(dú)給FruitActivity指定了FruitActivityTheme這個(gè)主題钦无,這樣我們就大功告成了逗栽。