Android开发学习 -- Day7 UI开发(三)

上一章讲到Android提供了一个更加强大的滚动控件 -- RecycleView。它可以说是一个增强版的ListView,不仅可以轻松的实现和ListView同样的效果,还优化了ListView中各种不足。目前Android官方更加推荐使用RecycleView。

1、RecycleView的基本用法

RecycleView属于新增的控件,为了让RecycleView在所有的版本上都能使用,Android团队采取了同样的方式,将RecycleView定义在了support库中。因此,想要使用RecycleView这个控件,首先需要在项目的Gradle中添加相应的依赖库才行。

打开app/build.gradle文件,在dependences闭包中添加'com.android.support:recyclerview-v7:26.1.0'

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation 'com.android.support:support-v4:26.1.0'
    implementation 'com.android.support:design:26.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:0.5'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    implementation 'com.android.support:recyclerview-v7:26.1.0'
}

添加完成之后记得Sync进行同步。然后修改xml代码,添加RecycleView:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.johnhao.listviewdemo.Activity.RecycleviewActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycle_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

布局中加入RecyclerView也很简单,指定一个id,然后将宽度和高度都设置成match_parent,这样RecyclerView就会占满整个布局空间。需要注意的是,由于RecyclerView并不是内置在系统SDK中,所以需要把完整的包路径写出来。

这里我们还是在之前的项目上进行修改,基于同样的水果用RecyclerView实现和ListView一样的效果。接下来为RecyclerView准备一个适配器。新建FruitAdapterAdv类,让这个适配器继承自RecyclerView.Adapter,并将泛型指定为FruitAdapterAdv.ViewHolder。其中ViewHolder是我们在FruitAdapterAdv当中的一个内部类,代码如下:

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

    private List<Fruit> mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder{

        View fruitView;
        ImageView fruitImg;
        TextView fruitName;

        public ViewHolder(View itemView) {
            super(itemView);
            fruitView = itemView;
            fruitImg = (ImageView) itemView.findViewById(R.id.fruit_image);
            fruitName = (TextView) itemView.findViewById(R.id.fruit_name);
        }
    }

    public FruitAdapterAdv(List<Fruit> fruitList){
        mFruitList = fruitList;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruits, parent, false);
        final ViewHolder holder = new ViewHolder(view);
        holder.fruitView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(), "You clicked view: " + fruit.getName(), Toast.LENGTH_SHORT).show();
            }
        });

        holder.fruitImg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(), "You click image: " + fruit.getName(), Toast.LENGTH_SHORT).show();
            }
        });
        return holder;
    }

    @Override
    public void onBindViewHolder(FruitAdapterAdv.ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.fruitImg.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

    @Override
    public int getItemCount() {
        return mFruitList.size();
    }
}

这里我们首先定义了一个内部类ViewHolder,ViewHolder要继承自RecyclerView.ViewHolder。然后ViewHolder的构造函数中需要传一个View参数,这个参数通常就是RecyclerView子项的最外层布局,那么我们就可以通过findViewById获得布局中的ImageView和TextView的实例了。此外,我们指定了一个fruitView,表示子布局的最外层布局。

接着往下看,FruitAdapterAdv也有一个构造函数,这个方法用于把数据传递源进来,并赋值给一个全局变量mFruitList,后面的操作都是在这个数据源的基础上进行。

由于FruitAdapterAdv是继承自RecyclerView.Adapter,所以就必须的重写onCreateViewHolder()、onBindViewHolder()和getItemCount()这3个方法。
□ onCreateViewHolder():通过LayoutInflater的inflate()方法将fruits布局加载进来。然后创建一个ViewHolder实例,并把加载的出来的布局传到构造函数中,最后将ViewHolder的实例返回。另外,我们在这里添加了点击事件的监听器。RecycleView的强大之处在这里也能得到体现,它可以轻松的实现子项中任意控件或者布局的点击事件。我们设置了子项中图片和子项最外层布局的点击事件,然后先获取用户点击的position,接着通过position拿到相应的Fruit实例,最后调用Toast分别弹出不同的内容加以区分。

□ onBindViewHolder():它是用于对RecycleView子项的数据进行赋值,通过position参数获取到当前Fruit实例,然后再将数据设置到ViewHolder的ImageView和TextView中。

□ getItemCount():指明RecycleView有多少个子项,这里我们直接返回数据源的长度即可。

适配器完成后,我们就可以开始使用RecycleView了,修改Activity内代码:

public class RecycleviewActivity extends BaseActivity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycleview);
        setTitle("Recycle View Demo");
        rv = (RecyclerView) findViewById(R.id.recycle_view);
        initFruits();
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        rv.setLayoutManager(layoutManager);
        FruitAdapterAdv fruitAdapterAdv = new FruitAdapterAdv(fruitList);
        rv.setAdapter(fruitAdapterAdv);
    }

    private void initFruits() {
        for (int i = 0; i < 20; i++) {
            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 cherry = new Fruit("Cherry", R.drawable.cherry_pic);
            fruitList.add(cherry);

            Fruit grape = new Fruit("Grape", R.drawable.grape_pic);
            fruitList.add(grape);

            Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
            fruitList.add(mango);

            Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
            fruitList.add(orange);

            Fruit pear = new Fruit("Pear", R.drawable.pear_pic);
            fruitList.add(pear);

            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 watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);
            fruitList.add(watermelon);
        }
    }
}

我们这里同样使用initFruits()方法来构造较多的数据。在onCreate()方法中,先渠道到RecycleView的实例,然后创建了一个LinearLayoutManager对象,并将它设置到RecycleView当中。LayoutManager用于指定RecycleView的布局方式,这里用的LinearLayoutManager是线性布局的意思,可以实现和ListView类似的效果。接下来我们创建了FruitAdapterAdv的实例,并将水果数据传到FruitAdapterAdv的构造函数中,最后调用RecycleView的setAdapter()方法来完成适配器的设置,这样RecycleView和数据之间的关联就建立完成了。

运行一下程序:


2、横向滚动

我们已经知道,ListView的扩展性不是特别优秀,如果想实现横着滚动的效果,就做不到了。但是使用RecycleView就能办得到,而且还非常简单。

首先需要修改一下fruits子项的布局,之前这个布局中的元素是水平的,这样适用于竖着滚动。但我们需要横着滚动,就应该把fruits的元素改成垂直排列。

<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:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"/>

</LinearLayout>

这里我们除了把LinearLayout的排列方向改了,宽度也做了调整,设置为100dp。因为考虑到有的水果名字长,有的水果名字短,如果wrap_content的话就会不统一,很难看。因此统一设置宽度为100dp。ImageView和TextView都设置成了水平居中显示,并且通过。layout_marginTop来给图片和文字间增加一些间距。

接下来修改Activity中的代码:

public class RecyclehorActivity extends BaseActivity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclehor);
        setTitle("Recycle View Horizontal");
        initFruits();
        rvh = findViewById(R.id.recycle_view_hor);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        rvh.setLayoutManager(layoutManager);
        FruitAdapterHor fruitAdapterHor = new FruitAdapterHor(fruitList);
        rvh.setAdapter(fruitAdapterHor);
    }
    ...
}

可以看到和刚才唯一不同的地方在于多添加了一行代码,调用了LinearLayoutManager的setOrientation()方法,我们传入了LinearLayoutManager.HORIZONTAL表示让布局横向排列,这样RecycleView就可以横向滚动了。(FruitAdapterHor是为了复习上面所学的,除了名字外其他都一样)

重新运行程序:


3、瀑布流

除了LinearLayoutManager之外,RecycleView还提供了GridLayoutManager和StaggeredLayoutManager这两种内置的布局排列方式。GridLayoutManager可以用于实现网格布局,StaggeredLayoutManager可以用于实现瀑布流布局。

修改xml布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android: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_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:layout_marginTop="10dp"/>

</LinearLayout>

将layout_width改回了match_parent,因为瀑布流的布局宽度应该根据布局的列数自动适配。另外,使用了layout_margin让子项之前有一点间距。另外,改了TextView为左对齐,因为我们要制造瀑布流就得“有长有短”。

修改Activity的代码:

public class RecycleviewStagActivity extends AppCompatActivity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycleview_stag);
        setTitle("Recycle View Staggered");
        initFruits();
        rvs = findViewById(R.id.recycle_view_stag);
        StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
        rvs.setLayoutManager(staggeredGridLayoutManager);
        FruitAdapterSta fruitAdapterSta = new FruitAdapterSta(fruitList);
        rvs.setAdapter(fruitAdapterSta);
    }

    private void initFruits() {
        for (int i = 0; i < 5; i++) {
            Fruit apple = new Fruit("Apple Apple Apple Apple Apple Apple Apple Apple", R.drawable.apple_pic);
            fruitList.add(apple);

            Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
            fruitList.add(banana);

            Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);
            fruitList.add(cherry);

            Fruit grape = new Fruit("Grape", R.drawable.grape_pic);
            fruitList.add(grape);

            Fruit mango = new Fruit("Mango Mango Mango Mango Mango Mango", R.drawable.mango_pic);
            fruitList.add(mango);

            Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
            fruitList.add(orange);

            Fruit pear = new Fruit("Pear", R.drawable.pear_pic);
            fruitList.add(pear);

            Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);
            fruitList.add(pineapple);

            Fruit strawberry = new Fruit("Strawberry Strawberry Strawberry Strawberry Strawberry Strawberry", R.drawable.strawberry_pic);
            fruitList.add(strawberry);

            Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);
            fruitList.add(watermelon);
        }
    }
}

首先,在onCreate()方法中,创建了一个StaggeredGridLayoutManager的实例,StaggeredGridLayoutManager的构造函数接收两个参数,第一个参数用于指定布局的列数,传入3表示会把布局分为3列;第二个参数用于指定布局的排列方向,传入StaggeredGridLayoutManager.VERTICAL表示会让布局纵向排列,最后再把创建好的实例设置到RecycleView中。后面为了能体现出瀑布流,我们把个别水果的名字加长了一下。

现在重新运行一下程序


关于RecycleView就先简单的学习到这里。

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

推荐阅读更多精彩内容