• Android 基础(四)滚动组件与Adapter


    Android滚动控件

    ListView的基本应用

    因为要展示大量数据,这就涉及到将数据注入到view中的过程。

    不过,数组中的数据是无法直接传递到view的,这需要借助适配器来实现。

    Android是完全遵循MVC模式设计的框架,Activity是Controller,layout是View,因为layout五花八门,很多数据都不能直接绑定上去,所以Android引入了Adapter这个机制作为复杂数据的展示的转换载体,所以各种Adapter只不过是转换的方式和能力不一样而已。
    几种常用的Adapter:

    展示简单的字符串ListView

    public class ListActivity extends BaseActivity {
    
        private String[] data = { "Apple","Banana","Orange","WaterMelon",
                "Pear","Grape","Pineapple","Strawberry","Strawberry","Strawberry","Strawberry",
                "Strawberry","Strawberry","Strawberry","Strawberry","Strawberry","Strawberry","Strawberry"
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_list);
            ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                    ListActivity.this, android.R.layout.simple_list_item_1,data
            );
            ListView listView = (ListView) findViewById(R.id.list_view);
            listView.setAdapter(adapter);
        }
    }
    

    上文中的android.R.layout.simple_list_item_1用的是android自带的布局,如果要自定义一个View,需要手写一个item的布局,然后写一个适配器,需要重写构造函数和getView方法。

    public class FruitAdapter extends ArrayAdapter<Fruit> {
    
        private int resourceId;
    
        public FruitAdapter(Context context, int textViewResourceId,
                            List<Fruit> objects) {
            super(context, textViewResourceId, objects);
            resourceId = textViewResourceId;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Fruit fruit = getItem(position); // 获取当前项的Fruit实例
            View view;
            ViewHolder viewHolder;
            if (convertView == null) {
               // 加载布局,listitem不需要添加父布局
                view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
               // new一个viewHolder,拥有对view中各个控件的引用
                viewHolder = new ViewHolder();
                viewHolder.fruitImage = (ImageView) view.findViewById (R.id.fruit_image);
                viewHolder.fruitName = (TextView) view.findViewById (R.id.fruit_name);
                view.setTag(viewHolder); // 将ViewHolder存储在View中
            } else {
                view = convertView;
                viewHolder = (ViewHolder) view.getTag(); // 重新获取ViewHolder
            }
            viewHolder.fruitImage.setImageResource(fruit.getImageId());
            viewHolder.fruitName.setText(fruit.getName());
            return view;
        }
    
        class ViewHolder {
    
            ImageView fruitImage;
    
            TextView fruitName;
    
        }
    
    }
    

    这里优化,当convertView为空的时候才去加载布局,创建view。如果不为空则直接用缓存中的view实例convertView,就省去了findbyview这种创建view实例的过程。通过获取viewHolder得到view实例中各个控件的引用,因为数据是不能复用的,所以需要重新set相关控件的数据。

    关于缓存:https://blog.csdn.net/Double2hao/article/details/49077739

    调用比较简单

     	@Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_list);
            initFruits();
            FruitAdapter adapter = new FruitAdapter(ListActivity.this,R.layout.fruit_item,fruitList);
            ListView listView = (ListView) findViewById(R.id.list_view);
            listView.setAdapter(adapter);
        }
    
        private void initFruits() {
            for (int i = 0; i < 10; ++i) {
                Fruit apple = new Fruit("Apple",R.drawable.img_1);
                fruitList.add(apple);
    
                Fruit banana = new Fruit("banana",R.drawable.img_2);
                fruitList.add(banana);
            }
        }
    

    RecyclerView的基本应用

    ListView是有很多缺点的,因为效率不高所以需要加很多优化。

    将上文的demo改成使用RecyclerView实现:

    1. 新建适配器,让这个适配器继承自RecyclerView.Adapter,并将泛型指定为新适配器的ViewHolder。
    2. ViewHolder是在适配器中定义的一个内部类,需要继承RecyclerView.ViewHolder
    3. ViewHolder的构造函数中要传入一个View参数,通常是RecyclerView子项最外层布局,于是就可以通过findViewById的方法来获取布局中的ImageViewTextView的实例。
    4. 因为继承自RecyclerView.Adapter,所以必须重写onCreateViewHolderonBindViewHoldergetItemCount
    public class RecyclerFruitAdapter extends RecyclerView.Adapter<RecyclerFruitAdapter.ViewHolder> {
    
        private List<Fruit> mFruitList; // 数据源,存实例对象
    
        public RecyclerFruitAdapter(List<Fruit> fruitList) {
            this.mFruitList = fruitList;
        }
    
        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
            final ViewHolder viewHolder = new ViewHolder(view);
            viewHolder.fruitView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Fruit fruit = mFruitList.get(viewHolder.getAdapterPosition());
                    Toast.makeText(v.getContext(), "you clicked view " + fruit.getName(), Toast.LENGTH_SHORT).show();
                }
            });
            viewHolder.fruitImage.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Fruit fruit = mFruitList.get(viewHolder.getAdapterPosition());
                    Toast.makeText(v.getContext(), "you clicked image " + fruit.getName(), Toast.LENGTH_SHORT).show();
                }
            });
            return viewHolder;
        }
    
        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            Fruit fruit = mFruitList.get(position);
            holder.fruitImage.setImageResource(fruit.getImageId());
            holder.fruitName.setText(fruit.getName());
        }
    
        @Override
        public int getItemCount() {
            return mFruitList.size();
        }
    
        static class ViewHolder extends RecyclerView.ViewHolder {
    
            View fruitView;
            ImageView fruitImage;
            TextView fruitName;
    
            public ViewHolder(@NonNull View itemView) {
                super(itemView);
                fruitView = itemView;
                fruitImage = (ImageView) itemView.findViewById(R.id.fruit_image);
                fruitName = (TextView) itemView.findViewById(R.id.fruit_name);
            }
    
        }
    
    }
    
    • onCreateViewHolder:是用来创建ViewHolder实例的,在这里将item布局加载,然后将加载好的view传入holder的构造函数中创建ViewHolder实例。
    • onBindViewHolder:是对item的数据进行赋值,会在每个子项被滚动到屏幕内的时候执行。通过position参数可以得到当前项的数据,即fruit实例。然后再对保存的ViewHolder中的fruitImagefruitName对象赋值。(省去获得实例的过程)
    • getItemCount:返回RecycleView的长度
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_recycle);
            initFruits();
            RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
            LinearLayoutManager layoutManager = new LinearLayoutManager(this);
            recyclerView.setLayoutManager(layoutManager);
            RecyclerFruitAdapter fruitAdapter = new RecyclerFruitAdapter(fruitList);
            recyclerView.setAdapter(fruitAdapter);
        }
    

    这里多了一个LayoutManager,用于指定RecyclerView的布局方式,这里使用LinearLayoutManager就是线性布局的意思。

    简单实现的xml如下:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 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=".Recycler.RecycleActivity">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"> #一个item宽度刚好是父布局的宽度,高度则用当前刚刚好包住里面的内容
    
        <ImageView
            android:id="@+id/fruit_image"
            android:layout_height="80dp"
            android:layout_width="100dp"
            />
    
        <TextView
            android:id="@+id/fruit_name"
            android:layout_marginLeft="8dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp" />
    
    </LinearLayout>
    

    横向滚动布局

    首先要修改fruit_item,将每个item的宽度设成一个固定的值,并将item里水平布局改成垂直的。

    <?xml version="1.0" encoding="utf-8"?>
    <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_height="50dp"
            android:layout_width="80dp"
            />
    
        <TextView
            android:id="@+id/fruit_name"
            android:layout_marginTop="10dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal" />
    
    </LinearLayout>
    

    在之前的调用基础上,加上LinearLayoutManagersetOrientatian方法来设置布局的排列方向。默认是纵向排列,传入LinearLayoutManager.HORIZONTAL表示让布局横向排列。

    layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
    

    瀑布流布局

    RecyclerView提供了GridLayoutManagerStaggeredGridLayoutManager两种内置的布局排列方式,前者可以实现网格布局,后者可以用于实现瀑布流布局。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_margin="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <ImageView
            android:id="@+id/fruit_image"
            android:layout_height="100dp"
            android:layout_width="match_parent"
            />
    
        <TextView
            android:id="@+id/fruit_name"
            android:layout_marginTop="10dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left" />
    
    </LinearLayout>
    

    这里因为每个item的宽度可以通过列数来自动适配,不是一个固定值,所以match_parent就行

    在调用时也比较简单,这里为了凸出效果,文字设定为随机长度

    public class RecycleActivity extends AppCompatActivity {
    
        private List<Fruit> fruitList = new ArrayList<>();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_recycle);
            initFruits();
            RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
            StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
            recyclerView.setLayoutManager(layoutManager);
            RecyclerFruitAdapter fruitAdapter = new RecyclerFruitAdapter(fruitList);
            recyclerView.setAdapter(fruitAdapter);
        }
    
        private void initFruits() {
            for (int i = 0; i < 10; ++i) {
                Fruit apple = new Fruit(getRandomLengthName("Apple"),R.drawable.img_1);
                fruitList.add(apple);
    
                Fruit banana = new Fruit(getRandomLengthName("banana"),R.drawable.img_2);
                fruitList.add(banana);
            }
        }
    
        private String getRandomLengthName(String name) {
            Random random = new Random();
            int length = random.nextInt(20) + 1;
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < length; i++) {
                builder.append(name);
            }
            return builder.toString();
        }
    }
    
    不要忘记努力,不要辜负自己 欢迎指正 QQ:1468580561
  • 相关阅读:
    logstash multiple piplines 配置方式
    filter-mutate过滤插件
    redis主从复制
    redis sentinel(哨兵)
    mongodb replica-set
    Linux入门篇(五)——Shell(一)
    Linux入门篇(四)——Vim的使用与Bash
    Linux入门篇(三)——文件与目录
    Linux入门篇(二)——文件
    Linux入门篇(一)——基本命令
  • 原文地址:https://www.cnblogs.com/smallocean/p/14448826.html
Copyright © 2020-2023  润新知