
歡迎大家挑錯(cuò),批評(píng)指正,謝謝
一、RecyclerView
我寫(xiě)過(guò)的幾乎所有的軟件里都有Rv,其重要性自然不言而喻。
其實(shí)基本用法和ListView沒(méi)有多大差距。
1.RecyclerView適配器
RecyclerView不再使用諸如SimpleAdapter、ArrayAdapter以及BaseAdapter,而是使用其專(zhuān)用的RecyclerView.Adapter,它實(shí)現(xiàn)了ViewHolder。
public class MyRvAdapter extends RecyclerView.Adapter<MyRvAdapter.ViewHolder>
{
List<Map<String,String>>movieList;
static class ViewHolder extends RecyclerView.ViewHolder{
public ViewHolder(View view){
//初始化控件
super(view);
}
}
public MyRvAdapter(List<Map<String,String>> movieList){
//將傳遞數(shù)據(jù)進(jìn)來(lái)
this.movieList = movieList;
}
@Override
public MyRvAdapter.ViewHolder onCreateViewHolder(ViewGroup p1, int p2)
{
// 為RecyclerView子項(xiàng)設(shè)置布局
if(mContext == null){
mContext = p1.getContext();
}
View view = LayoutInflater.from(mContext).inflate(R.layout.item,p1,false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(MyRvAdapter.ViewHolder p1, int p2)
{
// 為RecyclerVier填充數(shù)據(jù)、設(shè)置事件
}
@Override
public int getItemCount()
{
// 返回RecyclerView子項(xiàng)的數(shù)目
return movieList.size();
}
}
2.RecyclerView布局管理器
一共有三種布局形式
①LinearLayoutManager
效果與ListView一致
LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
參數(shù):Context,方向,數(shù)據(jù)是否倒序
②GridLayoutManager
效果與GridView一致
GridLayoutManager manager = new GridLayoutManager(this,2)
參數(shù):Context,列數(shù)
③StaggeredGridLayoutManager
瀑布流效果
StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
參數(shù):列數(shù),方向
3.RecyclerView定位及保存位置并恢復(fù)
在項(xiàng)目里有這樣一個(gè)需求:

用同一個(gè)Rv展示分類(lèi)數(shù)據(jù)和搜索結(jié)果,當(dāng)我退出搜索時(shí),精確恢復(fù)到搜索前的位置。
//搜索時(shí)保存位置
searchState = true;
View firstVirew = layoutManager.getChildAt(0);
savedTopHeight = firstVirew.getTop();
savedPosition = layoutManager.getPosition(firstVirew)
//searchState:當(dāng)前rv展示的是否為搜索結(jié)果
//firstView:當(dāng)前可見(jiàn)的第一個(gè)item
savedPosition:firstView在所有item中的位置
//savedTopHeight:firstView與RecyclerView頂部的距離
//關(guān)閉SearchView時(shí)恢復(fù)位置
if(searchState){
layoutManager.scrollToPositionWithOffset(savedPosition,savedTopHeight)
}
另外還有一個(gè)回到頂部的功能
//平滑滾動(dòng),有一個(gè)過(guò)渡動(dòng)畫(huà)
recyclerView.smoothScrollToPosition(position);
//滾動(dòng)到指定位置
recyclerView.scrollToPosition(position);
//另外還有scrollBy(int x,int y)和layoutManager.scrollToPositionWithOffset(int position,int offset)
這里我們用scrollToPosition(0)來(lái)實(shí)現(xiàn)回到頂部的功能
4.RecyclerView上拉加載(滑動(dòng)監(jiān)聽(tīng))
項(xiàng)目里的影片數(shù)據(jù)基于網(wǎng)頁(yè),一頁(yè)就那么幾個(gè)數(shù)據(jù)。當(dāng)我們滑動(dòng)到Rv底部時(shí),加載下一頁(yè)的數(shù)據(jù)并填充。
//設(shè)置滑動(dòng)監(jiān)聽(tīng)
rv.setOnScrollListener(new RecyclerView.OnScrollListener(){
public void onScrollStateChanged(RecyclerView recyclerView, int newState){
super.onScrollStateChanged(recyclerView,newState);
//要調(diào)用的回調(diào)方法當(dāng)RecyclerView滾動(dòng)狀態(tài)改變。
/**newState 一共有三種狀態(tài)
* SCROLL_STATE_IDLE代表RecyclerView現(xiàn)在不是滾動(dòng)狀態(tài)。
* SCROLL_STATE_DRAGGING代表RecyclerView處于被外力引導(dǎo)的滾動(dòng)狀態(tài),比如手指正在拖著進(jìn)行滾動(dòng)。
*SCROLL_STATE_SETTLING代表RecyclerView處于自動(dòng)滾動(dòng)的狀態(tài),此時(shí)手指已經(jīng)離開(kāi)屏幕,RecyclerView的滾動(dòng)是自身的慣性在維持。
* */
//不是滾動(dòng)狀態(tài)且能向下滾動(dòng)(即內(nèi)容多于一屏)
if (newState == RecyclerView.SCROLL_STATE_IDLE && rv.canScrollVertically(-1)){
//到底部=不能向上滾動(dòng)
//1能否向上滾動(dòng) -1能否向下滾動(dòng)
boolean isBottom = !rv.canScrollVertically(1);
//當(dāng)?shù)降撞壳覜](méi)有為RecyclerView加載數(shù)據(jù)(避免重復(fù)加載)
if(isBottom&&isLoading == false){
//當(dāng)所有數(shù)據(jù)加載完成
if(nextPage == null){
Snackbar.make(rv,"沒(méi)有更多了",Snackbar.LENGTH_SHORT)
.setAction("回頂部", new View.OnClickListener(){
@Override
public void onClick(View p1)
{
// TODO: Implement this method
rv.scrollToPosition(0);
}
})
.show();
return;
}
isLoading = true;
//為rv填充下一頁(yè)的數(shù)據(jù)
getHtmlCode(nextPage,"movieList");
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if(dy>20){
//向上滾動(dòng)距離大于20,隱藏回到頂部的按鈕
fab.setVisibility(View.GONE);
}
if(dy<-20){
//向下滾動(dòng)的距離超過(guò)20,顯示按鈕
fab.setVisibility(View.VISIBLE);
}
}
});
//滑動(dòng)監(jiān)聽(tīng)結(jié)束
二、Toolbar
Toolbar是一個(gè)非常靈活的控件,和design控件配合使用,可以通過(guò)很少的代碼實(shí)現(xiàn)很多炫酷的效果。而這些效果由我們自己通過(guò)自定義控件去實(shí)現(xiàn)的話(huà),是很困難的。
1.基本用法
布局中導(dǎo)入
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/colorPrimary"/>
Activity中使用
Toolbar toolBar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolBar);
2.給Toolbar添加菜單控件
在res/menu目錄下新建toolbar.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/classes"
android:orderInCategory="1"
android:title="分類(lèi)"
app:actionViewClass="android.widget.Spinner"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/search"
android:orderInCategory="2"
android:title="搜索"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="ifRoom"/>
</menu>
android:orderInCategory 優(yōu)先級(jí) 值越小越靠前
app:showAsAction 顯示情況
alaways 始終顯示
ifRoom 有空間時(shí)顯示
never 從不顯示 收在菜單里
3-1.和design控件配合使用
CoordinatorLayout做根布局
AppBarLayout做父布局
實(shí)現(xiàn)向上滑動(dòng)RecyclerView隱藏Toolbar,向下滑動(dòng)重現(xiàn)的效果
<android.support.design.widget.CoordinatorLayout
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="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="wrap_content"
android:background="?android:attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways|snap"
/>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
</android.support.design.widget.CoordinatorLayout>
只需要為RecyclerView設(shè)置app:layout_behavior="@string/appbar_scrolling_view_behavior"
使AppBarLayout子控件可以響應(yīng)其行為。
為T(mén)oolbar設(shè)置app:layout_scrollFlags="scroll|enterAlways|snap"
指定其響應(yīng)rv后的行為
app:layout_scrollFlags屬性詳情看這里
這兩個(gè)屬性是它們作為CoordinatorLayout控件的子控件才生效的。

2-2.可折疊式標(biāo)題欄

這個(gè)效果很酷,但是用design控件實(shí)現(xiàn)起來(lái)很簡(jiǎn)單。
<android.support.design.widget.CoordinatorLayout
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="match_parent"
android:fitsSystemWindows="true" >
<android.support.design.widget.AppBarLayout
android:id="@+id/detailsAppBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
>
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/detailsCollapsingToolbarLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?android:attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<ImageView
android:id="@+id/cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFAEAEAE"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax"
/>
</LinearLayout>
<android.support.v7.widget.Toolbar
android:id="@+id/detailsToolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
app:layout_collapseMode="pin"
/>
</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"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<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/detailsTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="10sp"
android:layout_margin="10dp" />
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/detailsFloatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:src="@drawable/ic_launcher"
app:layout_anchor="@id/detailsAppBar"
app:layout_anchorGravity="bottom|end"
/>
</android.support.design.widget.CoordinatorLayout>
依舊是CoordinatorLayout作為根布局,相比3-1的布局,appBarLayout內(nèi)又包裹了一層CollapsingToolbarLayout,CollapsingToolbarLayout又包裹了Toolbar和其他控件。
CollapsingToolbarLayout是一個(gè)增強(qiáng)型的FrameLayout,我們將其android:fitsSystemWindows設(shè)置為true,使其可以出現(xiàn)在狀態(tài)欄,但是必須將其所有的父布局都設(shè)置上并且將狀態(tài)欄設(shè)為透明才會(huì)生效。
為該Activity定義一個(gè)style 繼承自AppTheme 只是將狀態(tài)欄改成透明
<style name="DetailsTheme" parent="AppTheme">
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
在AndroidManifest.xml中為該活動(dòng)設(shè)置樣式
android:theme="@style/DetailsTheme"
對(duì)于NestedScrollView,和3-1布局RecyclerView一樣,設(shè)置了
app:layout_behavior="@string/appbar_scrolling_view_behavior"
但是響應(yīng)NestedScrollView的不再是Toolbar,而是CollapsingToolbarLayout,app:layout_scrollFlags的值也變了。因?yàn)槲覀儗?shí)現(xiàn)了不同的效果,滾動(dòng)到最后只剩下toolbar,所以用exitUntilCollapsed,使CollapsingToolbarLayout滾動(dòng)到最小高度。
至于CollapsingToolbarLayout內(nèi)子控件的app:layout_collapseMode屬性,是指定子控件在隨父控件滾動(dòng)折疊時(shí)的模式:“pin”:固定模式,在折疊的時(shí)候最后固定在頂端;“parallax”:視差模式,在折疊的時(shí)候會(huì)有個(gè)視差折疊的效果。
三.TabLayout+ViewPager+FragmentPagerAdapter
1.基本使用

這個(gè)選擇播放源和劇集的功能便是由TabLayout+ViewPager+FragmentPagerAdapter實(shí)現(xiàn),碎片的布局就是一個(gè)GridView。
當(dāng)初學(xué)習(xí)使用這個(gè)組合的時(shí)候,一臉懵逼,這也不懂那也不會(huì)。后來(lái)用了幾次,就縷清楚了。
先在Activity的布局里添加TabLayout和ViewPager,然后新建一個(gè)碎片或幾個(gè)碎片,根據(jù)數(shù)據(jù)類(lèi)型和功能為碎片寫(xiě)上布局,最后自定義一個(gè)FragmentPagerAdapter將ViewPager和Fragment二者聯(lián)系起來(lái)。
①添加控件
<android.support.design.widget.TabLayout
android:id="@+id/source_tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabSelectedTextColor="#ff7a61"
app:tabIndicatorHeight="0dp"
app:tabBackground="@color/tabBg"
app:tabMode="fixed"/>
<android.support.v4.view.ViewPager
android:id="@+id/movie_href_list_viewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp" />
②新建碎片及布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<GridView
android:id="@+id/detail_fragment_gridView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:numColumns="5" />
</LinearLayout>
public class DetailFragment extends Fragment
{
List<Map<String,String>> movieHrefList;
View view = null;
Context context;
GridView gridView;
public DetailFragment(List<Map<String,String>> movieHrefList){
//把要給GridView填充的數(shù)據(jù)傳遞進(jìn)來(lái)
this.movieHrefList = movieHrefList;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// TODO: Implement this method
//為碎片設(shè)置布局
if(view ==null)
view = inflater.inflate(R.layout.detail_fragment,container,false);
context = view.getContext();
gridView = (GridView) view.findViewById(R.id.detail_fragment_gridView);
//為GridView設(shè)置適配器:繪制布局、填充數(shù)據(jù) SimpleAdapter adapter = new SimpleAdapter(context,
movieHrefList,
R.layout.movie_href_item,
new String[] { "title" },
new int[] { R.id.movie_href_itemTextView });
gridView.setAdapter(adapter);
//返回碎片的布局
return view;
}
}
③自定義FragmentPagerAdapter將碎片添加到ViewPager中,并使TabLayout和ViewPager實(shí)現(xiàn)聯(lián)動(dòng)
public class DetailFragmentPagerAdapter extends FragmentPagerAdapter
{
//要添加的碎片
List<Fragment> fragList = new ArrayList<Fragment>();
//TabLayout的標(biāo)題
List<String> titleList = new ArrayList<String>();
public DetailFragmentPagerAdapter(FragmentManager fm,List<Fragment> fragList,List<String> titleList){
super(fm);
this.fragList = fragList;
this.titleList = titleList;
}
@Override
public int getCount()
{
// 返回ViewPager的頁(yè)數(shù)
return fragList.size();
}
@Override
public Fragment getItem(int p1)
{
// 返回每一個(gè)碎片
return fragList.get(p1);
}
@Override
public CharSequence getPageTitle(int position)
{
// 返回TabLayout的標(biāo)題
return titleList.get(position).toString();
}
}
④為ViewPager設(shè)置適配器,并實(shí)現(xiàn)與TabLayout的聯(lián)動(dòng)
DetailFragmentPagerAdapter adapter = new DetailFragmentPagerAdapter(getSupportFragmentManager(),fragList,movieSource);
viewPager.setAdapter(adapter);
//這一句呼應(yīng)適配器中重寫(xiě)的getPageTitle方法,如果沒(méi)有TabLayout,那么它們便不用寫(xiě)
tabLayout.setupWithViewPager(viewPager);
2.遇到的問(wèn)題
這里遇到的問(wèn)題不是這個(gè)組合所產(chǎn)生的,而是由于控件嵌套使用造成的,比如:
①NestedScrollView嵌套ViewPager造成的
ViewPager不顯示(猜想是高度為0,沒(méi)有繪制出來(lái))
不知道神馬原因,高度設(shè)置match或者wrap都不管用。
搜索一圈,解決方法如下:
設(shè)置NestedScrollView的fillViewPort屬性true|設(shè)置ViewPager為固定高度|動(dòng)態(tài)計(jì)算ViewPager內(nèi)容的高度并賦值
我選了第一個(gè),本來(lái)想選第三個(gè),那是最優(yōu)的解決辦法,但是實(shí)在是搞不出來(lái),以后有時(shí)間再說(shuō)。
②NestedScrollView嵌套GridView,GridView無(wú)法滾動(dòng)、顯示不全
典型的滑動(dòng)沖突,滑動(dòng)操作全部被NestedScrollView消費(fèi)掉了,解決辦法:
自定義NestedScrollView,重寫(xiě)onInterceptTouchEvent方法
public class DetailNestedScrollView extends NestedScrollView
{
public DetailNestedScrollView(Context context){
super(context);
}
public DetailNestedScrollView(Context context, AttributeSet attrs){
super(context,attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
// TODO: Implement this method
return false;
}
}
當(dāng)在其子控件上滑動(dòng)時(shí),不再由其消費(fèi)滑動(dòng)。
顯示不全,那就給子項(xiàng)的布局指定確切的高度。