zoukankan      html  css  js  c++  java
  • ListView

    ListView

    几乎每个APP都会用到滚动控件,淘宝的商品页,知乎的信息页等等,这是最重要而最难用的一种控件,所以从常用控件中单独提出来写用法
    滚动控件主要有两种,一种是安卓原带的ListView,另外一种是新增的RecyclerView,当然,在8012年的今天,我们更应该多关注RecyclerView。

    初始化

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

    ListView的数据传入需要适配器完成,比较好用的适配器之一是ArrayAdapter

    String[] data = {"some data"};
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, data);
    

    以字符串数据为例,这里由于数据为字符串,故将ArrayAdapter的泛型指定为String,ArrayAdapter有多个构造函数的重载,这里使用的第一个参数为上下文,第二个参数为布局的id,simple_list_item_1是一个安卓内置的布局文件,里面只有一个TextView,用于显示一段文本,第三个是数据

    最后将适配器传入

    ListView listView = findViewById(R.id.list_view);
    listView.setAdapter(adapter);
    

    定制界面

    和《第一行代码》一样,以一个显示水果的界面来作为例子

    首先我们要有一个实体类来描述水果的信息

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

    然后需要一个布局文件去确定信息怎么展示,创建fruit_item.xml

    <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_maginStart="10dp" />
    </LinearLayout>
    

    接下来创建一个自定义适配器去管理要传入的信息

    public class FruitAdapter extends ArrayAdapter<Fruit> {
    
        private int resourceId;
    
        public FruitAdapter (Context context, int FruitViewResourceId, List<Fruit> objects) {
            super(context, FruitViewResourceId,objects);
            resourceId = FruitViewResourceId;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Fruit fruit = getItem(position);
            View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
    
            ImageView fruitImage = view.findViewById(R.id.fruit_image);
            TextView fruitName = view.findViewById(R.id.fruit_name);
    
            fruitImage.setImageResource(fruit.getImageId());
            fruitName.setText(fruit.getName());
    
            return view;
        }
    }
    

    这段代码理解起来比较困难,首先从FruitAdapter看起,作为继承ArrayAdapter的构造函数,在这里将FruitViewResourceId记录下来,这是每一个水果的布局文件,也就是fruit_item.xml,以便后面动态加载布局文件

    getView()方法在每一个Fruit Item被划到屏幕内时会被触发,这个时候,首先通过通过getItem(position)得到被划进来的是哪个Fruit实体,然后利用LayoutInflater.from().inflate()方法动态加载对应的布局,在ListView中应使用getContext()得到Context,inflate的第一个参数是我们自定义的布局文件,而第三个参数为false,表示不为这个生成的View添加父布局,因为这个View是要放到ListView中的。

    最后为这个View添加好信息,需要注意,这里应该调用view的findViewById()方法,因为每一个Fruit实体的图像和名称是不同的

    总结一下就是,先取得水果布局文件ID,然后得到水果信息,根据水果信息构造出水果展示的布局,再把它加入到ListView中

    最后在onCreate中使用FruitAdapter

    Fruit apple = new Fruit("apple", R.drawable.apple_pic);
    fruitList.add(apple);
    //etc..
    FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
    listView.setAdapter(adapter);
    

    优化

    在动态加载布局时,假如用户是重复划动的,可以重用之前加载好的View

    View view;
    if(convertView != null){
        view = convertView;
    } else {
        view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
    }
    

    利用ViewHolder缓存控件实例

    class ViewHolder {
        ImageView fruitImage;
        TextView fruitName;
    }
    
    View view;
    ViewHolder viewHolder;
    if(convertView != null){
        view = convertView;
        viewHolder = (ViewHolder)view.getTag();
    } else {
        view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
        viewHolder = new ViewHolder();
        viewHolder.fruitImage.view.findViewById(R.id.fruit_image);
        viewHolder.fruitName.view.findViewById(R.id.fruit_name);
        view.setTag(viewHolder);
    }
    viewHolder.fruitImage.setImageResource(fruit.getImageId());
    viewHolder.fruitName.setText(fruit.getName());
    

    注意,setImageResource和setText仍然需要执行,因为fruit_item.xml中并没有这些信息

    点击事件

    ListView的点击监听器类似于Button的

    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            Fruit fruit = fruitList.get(position);
            //TODO
        }
    });
    

    RecyclerView

    RecyclerView相当于一个增强版的ListView,属于新增控件,要使用RecyclerView首先打开app/build.gradle文件,在denpendencies闭包中添加compile 'com.android.support:recyclerview-v7:xx',这里的xx填入你自己的版本号,然后就可以使用了

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

    适配器

    通常RecyclerView使用的适配器为自带的RecyclerView.Adapter。以《第一行代码》中的示例为例

    public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
    
        private List<Fruit> mFruitList;
    
        //内部类ViewHolder
        static class ViewHolder extends RecyclerView.ViewHolder{
            View fruitView;
            ImageView fruitImage;
            TextView fruitName;
    
            public ViewHolder(View view){
                super(view);
                fruitView = view;
                fruitImage = view.findViewById(R.id.fruitImage);
                fruitName = view.findViewById(R.id.fruitName);
            }
        }
    
        public FruitAdapter(List<Fruit> fruitList){
            mFruitList = fruitList;
        }
    
        //以下是三个RecyclerView.Adapter的方法的重写
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate
                    (R.layout.fruit_item,parent,false);
    
            return new ViewHolder(view);
        }
    
        @Override
        public void onBindViewHolder(ViewHolder holder, int position){
            Fruit fruit = mFruitList.get(position);
            holder.fruitName.setText(fruit.getName());
            holder.fruitImage.setImageResource(fruit.getImageID());
        }
    
        @Override
        public int getItemCount(){
            return mFruitList.size();
        }
    }
    

    ViewHolder的作用在上面就已经提过了。onCreateViewHolder()onBindViewHolder()getItemCount()三个方法必须被重写。
    onCreateViewHolder()中,构造了一个view并返回一个viewHolder,注意,这里的LayoutInflater有第三个参数false,原理同ListView。
    onBindViewHolder()会在每个子项被滑到屏幕内时被执行,可以通过position参数得到当前项的实例,并设置实例的Name和Image getItemCount()用于获取Recycler有多少项

    RecyclerView的使用和ListView类似

    RecyclerView recyclerView = findViewById(R.id.recyclerView);
    
    LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(layoutManager);
    
    FruitAdapter fruitAdapter = new FruitAdapter(fruitList);
    recyclerView.setAdapter(fruitAdapter);
    

    LinearLayoutManager用于指定RecyclerView的布局方式,他的参数就是this,因为RecyclerView是在当前页面呈现的。将LayoutManager传入recyclerView,再将携带了信息的Adapter也传入,就实现了类似ListView的视觉效果

    LayoutManager

    之所以说RecyclerView强于ListView,其中一点就是LayoutManager的多样性 LinearLayoutManager为线性布局,默认为纵向滚动,还可以实现横向滚动,只需要在设置了LayoutManager之前

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

    StaggeredGridLayoutManager可以实现瀑布流布局,瀑布流布局子项的宽度是根据布局的列数来确定的,所以宽度需要设置为match_parent,在代码中

    RecyclerView recyclerView = findViewById(R.id.recyclerView);
    
    StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
    recyclerView.setLayoutManager(layoutManager);
    
    FruitAdapter fruitAdapter = new FruitAdapter(fruitList);
    recyclerView.setAdapter(fruitAdapter);
    

    StaggeredGridLayoutManager的构造函数接收两个参数,第一个参数是布局的列数,第二个参数是布局的方向

    GridLayoutManager是网格布局,使用则同上。但是网格布局没有瀑布流布局好用

    点击事件

    与ListView很不一样的一点是,RecyclerView不能用setOnItemClickListener()注册子项的监听器,而只能在子项的View中去注册
    在RecyclerView的onCreateViewHolder()中注册点击事件

    @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);
    
        //给整个View设置监听器
        holder.fruitView.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                //TODO
            }
        });
    
        //给Image组件设置监听器
        holder.fruitImage.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                //TODO
            }
        });
    
        return new holder;
    }
    

    在监听器中,可以通过ViewHolder的getAdapterPosition()得到被点击的子项的位置,调用List的get(position)方法可以得到被点击的实例

  • 相关阅读:
    LeetCode 515. 在每个树行中找最大值(Find Largest Value in Each Tree Row)
    LeetCode 114. 二叉树展开为链表(Flatten Binary Tree to Linked List)
    LeetCode 199. 二叉树的右视图(Binary Tree Right Side View)
    LeetCode 1022. 从根到叶的二进制数之和(Sum of Root To Leaf Binary Numbers)
    LeetCode 897. 递增顺序查找树(Increasing Order Search Tree)
    LeetCode 617. 合并二叉树(Merge Two Binary Trees)
    LeetCode 206. 反转链表(Reverse Linked List) 16
    LeetCode 104. 二叉树的最大深度(Maximum Depth of Binary Tree)
    LeetCode 110. 平衡二叉树(Balanced Binary Tree) 15
    LeetCode 108. 将有序数组转换为二叉搜索树(Convert Sorted Array to Binary Search Tree) 14
  • 原文地址:https://www.cnblogs.com/hermitgreen/p/12649886.html
Copyright © 2011-2022 走看看