安卓开发小总结

柴钰棋

欢迎大家挑错,批评指正,谢谢

一、RecyclerView

我写过的几乎所有的软件里都有Rv,其重要性自然不言而喻。

其实基本用法和ListView没有多大差距。

1.RecyclerView适配器

RecyclerView不再使用诸如SimpleAdapter、ArrayAdapter以及BaseAdapter,而是使用其专用的RecyclerView.Adapter,它实现了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){
        //将传递数据进来
        this.movieList = movieList;
    }

    @Override
    public MyRvAdapter.ViewHolder onCreateViewHolder(ViewGroup p1, int p2)
    {
        // 为RecyclerView子项设置布局
        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填充数据、设置事件
    }

    @Override
    public int getItemCount()
    {
        // 返回RecyclerView子项的数目
        return movieList.size();
    }
}
2.RecyclerView布局管理器

一共有三种布局形式

①LinearLayoutManager
效果与ListView一致

LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);

参数:Context,方向,数据是否倒序

②GridLayoutManager
效果与GridView一致

GridLayoutManager manager = new GridLayoutManager(this,2)

参数:Context,列数

③StaggeredGridLayoutManager
瀑布流效果

StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);

参数:列数,方向

3.RecyclerView定位及保存位置并恢复

在项目里有这样一个需求:
电影分类下的数据

搜索结果

用同一个Rv展示分类数据和搜索结果,当我退出搜索时,精确恢复到搜索前的位置。

//搜索时保存位置
searchState = true;
View firstVirew = layoutManager.getChildAt(0);
savedTopHeight = firstVirew.getTop();
savedPosition = layoutManager.getPosition(firstVirew)

//searchState:当前rv展示的是否为搜索结果
//firstView:当前可见的第一个item
savedPosition:firstView在所有item中的位置
//savedTopHeight:firstView与RecyclerView顶部的距离
//关闭SearchView时恢复位置
if(searchState){
layoutManager.scrollToPositionWithOffset(savedPosition,savedTopHeight)
}

另外还有一个回到顶部的功能

//平滑滚动,有一个过渡动画
recyclerView.smoothScrollToPosition(position);
//滚动到指定位置
recyclerView.scrollToPosition(position);

//另外还有scrollBy(int x,int y)和layoutManager.scrollToPositionWithOffset(int position,int offset)

这里我们用scrollToPosition(0)来实现回到顶部的功能

4.RecyclerView上拉加载(滑动监听)

项目里的影片数据基于网页,一页就那么几个数据。当我们滑动到Rv底部时,加载下一页的数据并填充。

//设置滑动监听
        rv.setOnScrollListener(new RecyclerView.OnScrollListener(){

                public void onScrollStateChanged(RecyclerView recyclerView, int newState){
                    super.onScrollStateChanged(recyclerView,newState);
                    
                    //要调用的回调方法当RecyclerView滚动状态改变。
                    /**newState 一共有三种状态
                     * SCROLL_STATE_IDLE代表RecyclerView现在不是滚动状态。
                    * SCROLL_STATE_DRAGGING代表RecyclerView处于被外力引导的滚动状态,比如手指正在拖着进行滚动。
                    *SCROLL_STATE_SETTLING代表RecyclerView处于自动滚动的状态,此时手指已经离开屏幕,RecyclerView的滚动是自身的惯性在维持。
                     * */

                    //不是滚动状态且能向下滚动(即内容多于一屏)
                    if (newState == RecyclerView.SCROLL_STATE_IDLE && rv.canScrollVertically(-1)){
                        
                        //到底部=不能向上滚动
                        //1能否向上滚动 -1能否向下滚动
                        boolean isBottom = !rv.canScrollVertically(1);
                        
                        //当到底部且没有为RecyclerView加载数据(避免重复加载)
                        if(isBottom&&isLoading == false){
                            //当所有数据加载完成                         
                            if(nextPage == null){
                                Snackbar.make(rv,"没有更多了",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填充下一页的数据
                            getHtmlCode(nextPage,"movieList");
                        }
                    }
                }
                
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    if(dy>20){
                        //向上滚动距离大于20,隐藏回到顶部的按钮
                        fab.setVisibility(View.GONE);
                    }
                    if(dy<-20){
                        //向下滚动的距离超过20,显示按钮
                        fab.setVisibility(View.VISIBLE);
                    }
                }
                
            });
        //滑动监听结束

二、Toolbar

Toolbar是一个非常灵活的控件,和design控件配合使用,可以通过很少的代码实现很多炫酷的效果。而这些效果由我们自己通过自定义控件去实现的话,是很困难的。
1.基本用法

布局中导入
<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="分类"
        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 优先级 值越小越靠前

app:showAsAction 显示情况
alaways 始终显示
ifRoom 有空间时显示
never 从不显示 收在菜单里

3-1.和design控件配合使用
CoordinatorLayout做根布局
AppBarLayout做父布局
实现向上滑动RecyclerView隐藏Toolbar,向下滑动重现的效果

<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设置app:layout_behavior="@string/appbar_scrolling_view_behavior"
使AppBarLayout子控件可以响应其行为。
为Toolbar设置app:layout_scrollFlags="scroll|enterAlways|snap"
指定其响应rv后的行为
app:layout_scrollFlags属性详情看这里
这两个属性是它们作为CoordinatorLayout控件的子控件才生效的。

效果图

2-2.可折叠式标题栏


图片发自简书App

这个效果很酷,但是用design控件实现起来很简单。

<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内又包裹了一层CollapsingToolbarLayout,CollapsingToolbarLayout又包裹了Toolbar和其他控件。
CollapsingToolbarLayout是一个增强型的FrameLayout,我们将其android:fitsSystemWindows设置为true,使其可以出现在状态栏,但是必须将其所有的父布局都设置上并且将状态栏设为透明才会生效。

为该Activity定义一个style 继承自AppTheme 只是将状态栏改成透明
<style name="DetailsTheme" parent="AppTheme">
        <item name="android:statusBarColor">@android:color/transparent</item>
</style>
在AndroidManifest.xml中为该活动设置样式
android:theme="@style/DetailsTheme"

对于NestedScrollView,和3-1布局RecyclerView一样,设置了

app:layout_behavior="@string/appbar_scrolling_view_behavior"

但是响应NestedScrollView的不再是Toolbar,而是CollapsingToolbarLayout,app:layout_scrollFlags的值也变了。因为我们实现了不同的效果,滚动到最后只剩下toolbar,所以用exitUntilCollapsed,使CollapsingToolbarLayout滚动到最小高度。
至于CollapsingToolbarLayout内子控件的app:layout_collapseMode属性,是指定子控件在随父控件滚动折叠时的模式:“pin”:固定模式,在折叠的时候最后固定在顶端;“parallax”:视差模式,在折叠的时候会有个视差折叠的效果。

三.TabLayout+ViewPager+FragmentPagerAdapter

1.基本使用


图片发自简书App

这个选择播放源和剧集的功能便是由TabLayout+ViewPager+FragmentPagerAdapter实现,碎片的布局就是一个GridView。

当初学习使用这个组合的时候,一脸懵逼,这也不懂那也不会。后来用了几次,就缕清楚了。

先在Activity的布局里添加TabLayout和ViewPager,然后新建一个碎片或几个碎片,根据数据类型和功能为碎片写上布局,最后自定义一个FragmentPagerAdapter将ViewPager和Fragment二者联系起来。

①添加控件

<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填充的数据传递进来
        this.movieHrefList = movieHrefList;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        // TODO: Implement this method
        //为碎片设置布局
        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设置适配器:绘制布局、填充数据              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实现联动

public class DetailFragmentPagerAdapter extends FragmentPagerAdapter
{
    //要添加的碎片
    List<Fragment> fragList = new ArrayList<Fragment>();
    //TabLayout的标题
    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的页数
        return fragList.size();
    }

    @Override
    public Fragment getItem(int p1)
    {
        // 返回每一个碎片
        return fragList.get(p1);
    }

    @Override
    public CharSequence getPageTitle(int position)
    {
        // 返回TabLayout的标题
        return titleList.get(position).toString();
    }
}

④为ViewPager设置适配器,并实现与TabLayout的联动

DetailFragmentPagerAdapter adapter = new DetailFragmentPagerAdapter(getSupportFragmentManager(),fragList,movieSource);
viewPager.setAdapter(adapter);

//这一句呼应适配器中重写的getPageTitle方法,如果没有TabLayout,那么它们便不用写
tabLayout.setupWithViewPager(viewPager);

2.遇到的问题
这里遇到的问题不是这个组合所产生的,而是由于控件嵌套使用造成的,比如:
①NestedScrollView嵌套ViewPager造成的
ViewPager不显示(猜想是高度为0,没有绘制出来)
不知道神马原因,高度设置match或者wrap都不管用。
搜索一圈,解决方法如下:
设置NestedScrollView的fillViewPort属性true|设置ViewPager为固定高度|动态计算ViewPager内容的高度并赋值
我选了第一个,本来想选第三个,那是最优的解决办法,但是实在是搞不出来,以后有时间再说。
②NestedScrollView嵌套GridView,GridView无法滚动、显示不全
典型的滑动冲突,滑动操作全部被NestedScrollView消费掉了,解决办法:
自定义NestedScrollView,重写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;
    }
    
    
}

当在其子控件上滑动时,不再由其消费滑动。
显示不全,那就给子项的布局指定确切的高度。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容