第一行代码-ListView和RecycleView

来源:互联网 发布:淘宝二手怎么找不到了 编辑:程序博客网 时间:2024/06/08 06:20

ListView

ListView的简单用法

      ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内。同时屏幕上的所有数据则会滚出屏幕。

1.在布局中加入ListView

<ListViewandroid:id="@+id/list_view"android:layout_width="match_parent"android:layout_height="match_parent"/>


为ListView指定一个id,将宽度和高度设置为match_parent

2.在代码中使用ListView


将数据准备好,可以是从网上下载的,也可以是从数据库中读取的。例如定义一个字符串水果数组。

public class MainActivity extends AppCompatActivity {    String[] data= {"apple","banana", "orange", "watermelon", "pear", "grape", "pineapple", "strawberry",    "cherry", "mango",   "apple","banana", "orange", "watermelon", "pear", "grape", "pineapple", "strawberry"};    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ListView mListView = (ListView)findViewById(R.id.list_view);        ArrayAdapter<String>  adapter = new ArrayAdapter<String>(MainActivity.this , android.R.layout.simple_list_item_1,data);        mListView.setAdapter(adapter);    }}

数组中的数据是无法直接传递给ListView的,借助适配器来完成。Android提供了很多适配器的实现类,最好用的就是ArrayAdapter。可以通过泛型来指定要适配的数据类型,然后在构造函数中把要适配的数据传入。例如将ArrayAdapter的泛型指定为String,然后在ArrayAdapter的构造函数中依次传入当前上下文、ListView子项布局的id,以及要适配的数据。使用android.R.layout.simple.list_item_1作为ListView子项布局的id,是一个android内置的布局文件,里面只有一个TextView,可以用于简单的显示一段文本。
最后需要调用ListView的setAdapter()方法,将构建好的适配器对象传递进去。这样ListView和数据之间的关联就建立完成了。

定制ListView的界面


1.定义一个实体类,作为ListView适配器的适配类型。
例如:

Fruit类中只有两个字段,name表示水果的名字,imageId表示水果对应图片的资源id。
2.创建一个子项布局
为ListView创建一个自定义的子项布局,在layout目录下新建fruit_item.xml

在布局中定义了一个ImageView用于显示水果的图片,定义了一个TextView用于显示水果的名称。
3.自定义一个适配器继承自ArrayAdapter,将泛型指定为Fruit类,新建类FruitAdapter
public class FruitAdapter extends ArrayAdapter<Fruit> {    private int resourceId;    public FruitAdapter(@NonNull Context context, int resource, List<Fruit> objects) {        super(context, resource, objects);        this.resourceId = resource;    }    @NonNull    @Override    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {        Fruit  fruit = getItem(position);        View  view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);        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.getTitle());        return view;    }}


FruitAdapter重写了父类的构造函数,用于将上下文、ListView子项布局的id和数据都传递进来。重写了getView()方法,这个方法在每个子项被滚动到屏幕内的时候调用。在getView()方法中,首先通过getItem()方法得到当前项的Fruit实例,然后使用LayoutInflater来为这个子项传入布局。
LayouInflater的inflate()方法接收3个参数,第一参数是要加载布局的文件的id.第二个参数是给加载好的布局再添加一个父布局,第三个参数指定为false,表示让在父布局中声名的layout生效。但不为这个View添加父布局,因为一旦View有了父布局之后,就不能添加到ListView中了。当做ListView的标准写法。
View的findViewById()方法分别获取到ImageView和TextView的实例,分别调用setAImageResource()和setText()方法来设置显示的图片和文字,最后将布局返回。

3.修改Activity代码,例如

public class MainActivity extends AppCompatActivity {    String[] data= {"apple","banana", "orange", "watermelon", "pear", "grape", "pineapple", "strawberry",    "cherry", "mango",   "apple","banana", "orange", "watermelon", "pear", "grape", "pineapple", "strawberry"};    ArrayList<Fruit> fruitList = new ArrayList<>();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initFruits();        ListView mListView = (ListView)findViewById(R.id.list_view);        //ArrayAdapter<String>  adapter = new ArrayAdapter<String>(MainActivity.this , android.R.layout.simple_list_item_1,data);        FruitAdapter adapter = new FruitAdapter(MainActivity.this , R.layout.fruit_item, fruitList);        mListView.setAdapter(adapter);    }    private void initFruits() {        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.banana_pic);            fruitList.add(banana);            Fruit orange = new Fruit("Orange", R.drawable.orange_pic);            fruitList.add(orange);            Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);            fruitList.add(watermelon);            Fruit pear = new Fruit("Pear", R.drawable.pear_pic);            fruitList.add(pear);            Fruit grape = new Fruit("Grape", R.drawable.grape_pic);            fruitList.add(grape);            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 cherry = new Fruit("Cherry", R.drawable.cherry_pic);            fruitList.add(cherry);            Fruit mango = new Fruit("Mango", R.drawable.mango_pic);            fruitList.add(mango);        }    }}

在onCreate()方法中首先初始化了数据源,然后创建了FruitAdapter对象,并将Adapter作为适配器传递给ListView

提升ListView的运行效率
ListView很难用是因为有许多细节可以优化,其中很重要的一点是运行效率。例如FruitAdapter的getView()
方法每次都将布局重新加载了一遍,当ListView快速滚动时,会成为性能瓶颈。

getView()方法中提供了convertView参数,用于将之前加载好的布局缓存,以便之后重用。
 public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {        Fruit  fruit = getItem(position);        View  view;                if(convertView == null)        {               view = LayoutInflater.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.getTitle());        return view;    }


在getView()方法中进行判断,当convertView为null,则使用LayoutInflater加载布局,如果不为null
就直接对convertView进行重用,提高了ListView的运行效率。优化了性能。
虽然现在不用重复去加载布局,但是在getView()方法中还是会调用View的findViewById()方法来获取控件的实例。
可以通过ViewHolder来对这部分性能优化。

    @Override    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {        Fruit  fruit = getItem(position);        View  view;        ViewHolder viewHolder;        if(convertView == null)        {               view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);               viewHolder = new ViewHolder();               viewHolder.fruitImage =  (ImageView) view.findViewById(R.id.fruit_image);;               viewHolder.fruitName =  (TextView) view.findViewById(R.id.fruit_name);;               view.setTag(viewHolder);        }        else        {            view =convertView;            viewHolder = (ViewHolder)view.getTag();        }        viewHolder.fruitImage.setImageResource(fruit.getImageId());        viewHolder.fruitName.setText(fruit.getTitle());        return view;    }    class  ViewHolder    {        ImageView fruitImage;        TextView fruitName;    }


借助内部类ViewHolder来对控件的实例进行缓存。当convertView为null的时候,创建一个ViewHolder对象,
将控件的实例存放在ViewHolder里,然后调用View的setTag()方法,将ViewHolder对象存储在View中。当convertView不为null的时候调用View的getTag()方法,重新取出。


4.ListView的点击事件



ListView中实现ListView子项的点击事件
    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initFruits();        ListView mListView = (ListView)findViewById(R.id.list_view);        //ArrayAdapter<String>  adapter = new ArrayAdapter<String>(MainActivity.this , android.R.layout.simple_list_item_1,data);        FruitAdapter adapter = new FruitAdapter(MainActivity.this , R.layout.fruit_item, fruitList);        mListView.setAdapter(adapter);        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {                Fruit fruit = fruitList.get(i);                Toast.makeText(MainActivity.this, fruit.getTitle(), Toast.LENGTH_SHORT).show();            }        });    }


使用setOnItemClickListener()方法为ListView注册监听器,当用户点击ListView中的任何一个子项时,就会回调onItemClick()方法。可以通过position参数判断用户点击的哪一个子项。

RecyclerView



Android提供了强大的滚动控件-RecyclerView.可以轻松实现ListView的效果还优化了ListView中的各种不足。

目前Android推荐使用RecyclerView。

1.依赖库

RecyclerView定义在了support库中,所以需要在build.gradle中添加相应的依赖库。

打开app/build.gradle文件,在dependencies闭包中添加如下内容:


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


2.布局中加入RecyclerView


布局中加入RecyclerView
并定义一个RecyclerView的id,

    <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>

3.为RecyclerView准备适配器。


新建一个ecyclerView的子项布局fruit_item。

<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_vertical"        android:layout_marginLeft="10dp"/></LinearLayout>
然后构建FruitAdapter适配器继承RecyclerView.Adapter.

    public class  FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>    {        List<Fruit> mFruitList;        class ViewHolder extends  RecyclerView.ViewHolder        {            ImageView fruitImage;            TextView fruitName;            private ViewHolder(View v)            {                super(v);                fruitImage = v.findViewById(R.id.fruit_image);                fruitName = v.findViewById(R.id.fruit_name);            }        }        public FruitAdapter(List<Fruit> fruitList) {            mFruitList = fruitList;        }        @Override        public FruitAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);          ViewHolder mViewHolder= new ViewHolder(v);            return mViewHolder;        }        @Override        public void onBindViewHolder(ViewHolder holder, int position) {            Fruit mfruit = mFruitList.get(position);            holder.fruitImage.setImageResource(mfruit.getImageId());            holder.fruitName.setText(mfruit.getTitle());        }        @Override        public int getItemCount() {            return mFruitList.size();        }    }



新建FruitAdapter类,让这个适配器继承自RecyclerView.Adapter,并将
泛型指定为FruitAdapter.ViewHolder。其中,ViewHolder是定义在FruitAdapter中定义的内部类。


 首先定义了内部类ViewHolder,ViewHolder继承自RecyclerView.ViewHolder。ViewHolder的构造函数要传入一个View参数,这个参数是RecyclerView子项的最外层布局。通过findViewById()方法来获取ImageView和TextView的实例。


FruitAdapter中也有一个构造函数,用于把要展示的数据传递进来。并且赋值给一个全局变量mFruitList.
由于FruitAdapter继承自RecyclerView.Adapter的,必须重写onCreateViewHolder()、onBindViewHolder()和getItemCount()这3个方法。onCreateViewHolder()用于创建ViewHolder实例。在这个方法中将fruit_item加载进来。然后创建一个ViewHolder实例,将加载出来的布局传入构造函数中,最后将ViewHolder的实例返回。onBindViewHolder()方法是用于对RecyclerView子项的数据进行赋值,会在每个子项被滚动到屏幕内的时候执行。通过position参数得到当前项的Fruit实例。将数据设置到ViewHolder的ImageView和TextView中即可。getItemCount()用于告诉RecyclerView有多少子项,直接返回数据源长度。


4.使用RecyclerView



        RecyclerView mRecyclerView =(RecyclerView)findViewById(R.id.recycler_view);        //ArrayAdapter<String>  adapter = new ArrayAdapter<String>(MainActivity.this , android.R.layout.simple_list_item_1,data);        LinearLayoutManager layoutManager = new LinearLayoutManager(this);        mRecyclerView.setLayoutManager(layoutManager);        FruitAdapter adapter = new FruitAdapter(fruitList);        mRecyclerView.setAdapter(adapter);




在onCreate()方法中获取RecyclerView的实例,然后创建一个LinearLayoutManager对象,将它设置到RecyclerView当中。LayoutManager用于指定RecyclerView的布局方式。使用LinearLayoutManager是线性布局的意思。创建FruitAdapter的实例,将水果数据传入到FruitAdapter的构造函数中。最后调用RecyclerView的setAdapter()方法来完成适配器设置。这样RecyclerView和数据之间的关联就完成了。


RecyclerView实现横向滚动效果通过调用LinearLayoutManager的setOrientation()方法来设置布局的排列方法。默认是纵向排列的。传入LinearlayoutManager.HORIZONTAL来表示布局横向排列。

        RecyclerView mRecyclerView =(RecyclerView)findViewById(R.id.recycler_view);        //ArrayAdapter<String>  adapter = new ArrayAdapter<String>(MainActivity.this , android.R.layout.simple_list_item_1,data);        LinearLayoutManager layoutManager = new LinearLayoutManager(this);        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);        mRecyclerView.setLayoutManager(layoutManager);        FruitAdapter adapter = new FruitAdapter(fruitList);        mRecyclerView.setAdapter(adapter);



由于子项布局的宽度设置为match_parent,设置为横向布局出现了一个图片占据了屏幕,做如下修改。

RecyclerView提供了GridLayoutManager和StaggeredGridLayoutManager两种内置的布局排列方式。GridLayoutManager用于实现网格布局,StaggeredGridLayoutManager用于实现瀑布流布局。

        RecyclerView mRecyclerView =(RecyclerView)findViewById(R.id.recycler_view);        //ArrayAdapter<String>  adapter = new ArrayAdapter<String>(MainActivity.this , android.R.layout.simple_list_item_1,data);   //     LinearLayoutManager layoutManager = new LinearLayoutManager(this);    //    layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);        StaggeredGridLayoutManager layoutManager =new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);        mRecyclerView.setLayoutManager(layoutManager);        FruitAdapter adapter = new FruitAdapter(fruitList);        mRecyclerView.setAdapter(adapter);



  在onCreate()方法中,创建了一个StaggeredGridLayoutManager的实例。StaggeredGridLayoutManager的构造函数接收两个参数,第一个参数用于指定布局的列数,传入3表示会把布局分成3列;第二个参数指定布局的排列方式,传如LayoutManager.VERTICAL表示会将布局纵向排列。最后将创建好的实例设置到RecyclerView当中。
当指定的列数过大时,多个RecyclerView的子项便会挤压在一起。

        RecyclerView mRecyclerView =(RecyclerView)findViewById(R.id.recycler_view);        //ArrayAdapter<String>  adapter = new ArrayAdapter<String>(MainActivity.this , android.R.layout.simple_list_item_1,data);   //     LinearLayoutManager layoutManager = new LinearLayoutManager(this);    //    layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);     //   StaggeredGridLayoutManager layoutManager =new StaggeredGridLayoutManager(10,StaggeredGridLayoutManager.VERTICAL);        GridLayoutManager layoutManager = new GridLayoutManager(MainActivity.this, 3);        mRecyclerView.setLayoutManager(layoutManager);        FruitAdapter adapter = new FruitAdapter(fruitList);        mRecyclerView.setAdapter(adapter);


GridLayoutManager的构造函数中第一参数是context,第二个参数是整型列数。
RecyclerView的点击事件

在RecyclerView中注册点击事件。


        class ViewHolder extends  RecyclerView.ViewHolder        {            ImageView fruitImage;            TextView fruitName;            View fruitView;            private ViewHolder(View v)            {                super(v);                fruitImage = v.findViewById(R.id.fruit_image);                fruitName = v.findViewById(R.id.fruit_name);                fruitView = v;            }        }
@Override public FruitAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false); final ViewHolder mViewHolder= new ViewHolder(v); mViewHolder.fruitView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int position = mViewHolder.getAdapterPosition(); Fruit mFruit = mFruitList.get(position); Toast.makeText(view.getContext(), "you clicked View" + mFruit.getTitle(),Toast.LENGTH_SHORT).show(); } }); mViewHolder.fruitImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int position = mViewHolder.getAdapterPosition(); Fruit mFruit = mFruitList.get(position); Toast.makeText(view.getContext(), "you clicked image" + mFruit.getTitle(),Toast.LENGTH_SHORT).show(); } }); mViewHolder.fruitName.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int position = mViewHolder.getAdapterPosition(); Fruit mFruit = mFruitList.get(position); Toast.makeText(view.getContext(), "you clicked name" + mFruit.getTitle(),Toast.LENGTH_SHORT).show(); } }); return mViewHolder; }






先修改了ViewHolder,在ViewHolder中添加了fruitView变量来保存子项最外层布局的实例,然后在onCreateViewHolder()方法中注册点击事件。这里为最外层和ImageView和TextView都注册了点击事件。可以轻松实现子项中任意控件或点击事件。











原创粉丝点击