ListView

一、定制ListView界面

1、定义实体类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;
    }
}

2、新建fruit_item.xml,为ListView子项指定自定义布局。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
   <ImageView
        android:id="@+id/fruit_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
<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>

3、创建自定义适配器,该适配器继承自ArrayAdapter,并将泛型指定为Fruit类。

public class FruitAdapter extends ArrayAdapter<Fruit> {
    private int resourceId;
// FruitAdapter重写了一组构造器,将上下文,ListView子项布局的id与数据传递进来
    public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
        super(context, textViewResourceId, objects);
        resourceId = textViewResourceId;
    }
//重写getView()方法,该方法在每个子项滚动到屏幕内时被调用。
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Fruit fruit = getItem(position);   //通过getItem()方法获得Fruit实例
//为子项加载布局
        View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
//第一个参数:加载的布局文件的id。
//第二个参数:给待加载的布局添加一个父布局。
//第三个参数:一般为false,表示只让父布局中声明的layout属性生效,但不会为该View添加父布局。
        TextView fruitName = view.findViewById(R.id.fruit_name);
        ImageView fruitImage = view.findViewById(R.id.fruit_image);
        fruitName.setText(fruit.getName());
        fruitImage.setImageResource(fruit.getImageId());
        return view;
    }
}

4、在MainActivity中使用ListView,把待适配的数据传入给Adapter,最后调用setAdapter()方法将构建好的适配器传入ListView,完成数据与ListView间的关联。

public class MainActivity extends AppCompatActivity {
    private List<Fruit> fruitsList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits();   //初始化数据
//一般使用ListView时,数据无法直接传递给ListView,所以要借助适配器Adapter完成。
        FruitAdapter adapter = new 
FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitsList);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);
    }
    private void initFruits() {
            Fruit apple = new Fruit("Apple",R.drawable.chair);
            fruitsList.add(apple);
            ……    //添加数据,此处省略
    }
}

 

二、提升ListView的运行效率

        使用ListView时,其运行效率很低。在FruitAdapter的getView()方法中,每次都会将布局加载一遍。当ListView快速滚动时,性能就会成为问题。

1、使用getView()中convertView参数对加载好的布局进行缓存。

2、每次在getView()方法中还是会调用View的findViewById()方法来获取一次实例,所以借助ViewHolder对控件实例进行缓存。

 

三、设定ListView的点击事件

MainActivity中添加:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
       public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
             Fruit fruit = fruitsList.get(position);
             Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_LONG).show();
            }
        });

setOnItemClickListener()方法为ListView注册了一个监听,当用户点击ListView中任何一个Item时,就会调用onItemClick()方法。

 

RecyclerView

        RecyclerView与ListView相比性能进行了优化,同时扩展性增强,如可以实现横向滚动。

        ListView的布局排列是由自身去管理,而RecyclerView中是运用LayoutManager管理,LayoutManager中制定了一套可扩展的布局排列接口,通过实现不同接口可以定制不同排列方式。

一、定制RecyclerView界面

1、在app/build.gradle文件中,dependencies闭包中添加:

compile 'com.android.support:recyclerview-v7:26.0.0-alpha1'

2、activity_main.xml文件中运用完整包路径添加RecyclerView:

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

3、Fruit实体类与fruit_item.xml同ListView

4、新建适配器类FruitAdapter类,继承自RecyclerView.Adapter,泛型为FruitAdapter.ViewHolder,其中FruitAdapter为一个内部类。

public class FruitAdapter extends RecyclerView.Adapter <FruitAdapter.ViewHolder>{
    private List<Fruit> mFruitList;
    static class ViewHolder extends  RecyclerView.ViewHolder{
        ImageView fruitImage;
        TextView fruitName;
//RecyclerView子项的最外层布局,通过其可获得ImageView与TextView
        public ViewHolder(View view) {
            super(view);
            fruitImage = view.findViewById(R.id.fruit_image);
            fruitName = view.findViewById(R.id.fruit_name);
        }
}
//用于获取展示的数据源
    public FruitAdapter(List<Fruit> fruitList) {
        mFruitList = fruitList;
}

//用于创建ViewHolder实例
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = 
LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
ViewHolder holder = new ViewHolder(view);
        return holder;
}
//用于对RecyclerView子项进行赋值,当前子项滚动到屏幕时,会通过position得到当前子项的Fruit实例,然后设置到ImageView与TextView中。
    @Override
    public void onBindViewHolder(ViewHolder holder, int position){
        Fruit fruit = mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
}
//告诉RecyclerView一共有多少子项
    @Override
    public int getItemCount() {
        return mFruitList.size();
    }
}

5、在MainActivity中使用RecyclerView,用法与ListView类似

public class MainActivity extends AppCompatActivity {
     private List<Fruit>fruitsList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits();     //初始化数据源
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
// LayoutManager用于指定RecyclerView的布局方式
        LinearLayoutManager layoutManager = new 
LinearLayoutManager (this);
        recyclerView.setLayoutManager(layoutManager);
        FruitAdapter adapter = new FruitAdapter(fruitsList);
        recyclerView.setAdapter(adapter);
}
    private void initFruits() {
            Fruit apple = new Fruit("Apple",R.drawable.chair);
            fruitsList.add(apple);  
            …….          添加数据,此处省略
    }
}

二、实现横向滚动

1、修改fruit_item.xml文件中的布局

2、

LinearLayoutManager layoutManager = new LinearLayoutManager (this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);

三、实现瀑布流布局

1、修改fruit_item.xml文件中的布局

2、

//第一个参数:指定布局列数
//第二个参数:指定布局排列方向
StaggeredGridLayoutManager layoutManager = new  
  StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);

四、RecyclerView的点击事件

        ListView的点击事件缺陷:ListView通过setOnItemClickListener()为子项注册点击事件,无法为子项中具体的空间分别指定点击事件。

        RecyclerView摒弃了这种缺陷,所有的点击事件都必须由每个Item中具体的View去注册监听。可以轻松实现子项中任意控件或布局的点击事件。

@Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = 
LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
        final ViewHolder holder = new ViewHolder(view);
//为子项最外层的布局实例fruitView注册监听
        holder.fruitView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
            }
        });
//为子项中的图片fruitImage注册监听
        holder.fruitImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
            }
        });
        return holder;
    }