自定义控件和ListView,RecyclerView

这篇博客介绍了如何在Android中创建自定义控件,包括继承LinearLayout并重写构造函数,以及在布局中引入。接着讲解了ListView的基本用法、界面定制、性能优化和点击事件处理。对于RecyclerView,文章详细阐述了其基本用法、实现横向滚动和瀑布流布局,以及如何添加点击事件。此外,还提供了实例练习链接帮助读者深入理解。
摘要由CSDN通过智能技术生成

引入布局

Android:background可以为布局或者控件指定一个背景;
Android:layout_margin可以指定控件在上下左右方向上偏移的距离,也可以使用android:layout_marginLeft等属性来单独指定控件在某个方向上偏移的距离。
成功定义一个布局之后,在主布局文件里面用将其引入;

在这里插入图片描述
隐藏系统自带标题栏的方式:调用gerSupportActionBar()来获得ActionBar实例,然后调用ActionBar的hide()将标题栏隐藏起来。

在这里插入图片描述

创建自定义控件

1.新建类继承LinearLayout,成为自定义控件;
2.重写LinearLayout里面带有两个参数的构造函数,在布局里面引入自定义控件就会调用这个构造函数,通过LayoutInflater的from()方法可以构建出一个LayoutInflater对象,然后调用inflate()方法可以动态加载一个布局文件,inflater()方法接收两个参数,(第一个参数是要加载布局文件的id,第二个参数是给加载好的布局再添加一个父布局);
在这里插入图片描述

3.在布局文件里面添加自定义控件;
在这里插入图片描述

ListView

ListView的简单用法

1.在.xml里面加入ListView布局;
2.在类里面定义数据。然后将数据传递给ListView,这里借助适配器完成:
将ArrayAdapter泛型指定成String;
在ArrayAdapter的构造函数中依次传入当前上下文,ListView子项布局的id,要适配的数据;
调用ListView的setAdapter()方法,将构建好的适配器对象传递进去;

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

定制ListView的界面

1.定义一个实体类,作为ListView适配器的适配类型;
2.为ListView的子项指定一个自定义布局;
3.创建一个自定义的适配器,继承ArrayAdapter,泛型指定为刚刚自定义的实体类;在新建的适配器里重写了父类的一组构造函数,将上下文,ListView子项布局的id,数据都传递进去;重写getView()方法,首先通过getItem()得到当前的实例,然后通过LayoutInflater来为这个子项加载我们传入的布局;
LayoutInflater的inflate()接收三个参数,第三个参数是false是表示只让我们在父布局中声明layout属性生效,不会为这个View添加父布局,因为一旦View有了父布局之后,他就不能再添加到ListView中了。

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 = 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.getName()); 
return view; 
} 
}

提升ListView的运行效率

在getView方法里面,每次都将布局重新加载了一遍,当ListView快速滚动的时候,这就会成为性能的瓶颈。
在getView()方法里面还有一个convertView参数,这个参数是用于将之前加载好的布局进行缓存,以便之后可以进行重用。
我们可以在getView()方法里面进行判断,如果convertView为null,那么就使用LayoutInflater去加载布局,如果不是null则直接对convertView进行复用;

public class FruitAdapter extends ArrayAdapter<Fruit> {
    ...
    @Override
    public View getView(int position, View convertView, 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.getName());
        return view;
    }
}

在此优化基础上,虽然现在不会再重复去加载布局了,但是在每次getView()方法里面还是会去调用View的findViewById来获取一次控件的实例,这时我们可以借助ViewHolder来对这部分性能进行优化;
新增一个内部类ViewHolder,用于对控件的实例进行缓存,当convertView为null的时候,创建一个ViewHolder对象,将控件的实例都存在ViewHolder里面,然后调用View的setTag()方法,将ViewHolder对象存储在View中,当convertView不为null的时候,则调用View的getTag()方法,将ViewHolder重新取出来(这样控件存在ViewHolder里面就不用每次通过findViewById去获取控件实例了)。

public class FruitAdapter extends ArrayAdapter<Fruit> {
    ...
    @Override
    public View getView(int position, View convertView, 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); // 将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;
    }}

ListView的点击事件

使用setOnItemClickListener()为ListView注册一个监听器,当用户点击了ListView里面任意一个子项,就回调onItemClick()方法,然后通过position参数判断用户点击的是哪一个子项,然后获取对应的实例,用Toast显示出来。

public class MainActivity extends AppCompatActivity {
    private List<Fruit> fruitList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits();
        FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.
                fruit_item, fruitList);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                                    int position, long id) {
                Fruit fruit = fruitList.get(position);
                Toast.makeText(MainActivity.this, fruit.getName(),
                        Toast.LENGTH_SHORT).show();
            }
        });
    }
    ...
}

RecyclerView

RecyclerView的基本用法

1.在build.gradle里添加相应的依赖库;
在这里插入图片描述

2.在布局文件里加入RecyclerView,因为它不是内置在系统SDK里面的,所以需要把完整的包路径写出来;
在这里插入图片描述

3.为RecyclerView准备一个适配器,新建一个类,让这个适配器继承RecyclerView.Adapter,将其泛型指定为类名.ViewHolder,ViewHolder时我们在类里定义的一个内部类,这个内部类继承RecyclerView.ViewHolder,ViewHolder的构造函数里面传入一个View参数(这个参数通常是RecyclerView子项的最外层布局);
4.由于这个类时继承RecyclerView.Adapter的,那么就要重写onCreateViewHolder(),onBindViewHolder(),getItemCount()这三个方法。
在这里插入图片描述

onCreateViewHolder()是用于创建ViewHolder实例的,在这里面可以将自定义布局传进去,然后创建一个ViewHolder实例,把加载出来的布局传入到构造函数里面,最后将ViewHolder实例返回;
onBindViewHolder()是用于对RecyclerView子项的数据进行赋值的,会在每个子项被滚动到屏幕内的时候执行,通过position参数得到当前项的实例,然后再将数据设置到ViewHolder的对应数据里面;
getItemCount()用于告诉RecyclerView一共有多少个子项,直接返回数据源的长度就可以了;

5.在MainActivity函数里面,使用了一个initFruits()方法,用于初始化所有数据,然后再OnCreate()方法里面先获取到了RecyclerView的实例,然后创建了一个LinearLayoutManager对象,并且将它设置到RecyclerView中。(LayoutManager用于指定RecyclerView的布局方式,这里LinearLayoutManager是线性布局的意思,可以实现和ListView类似的效果),然后创建了适配器的实例,将数据传入到适配器的构造函数里面,最后调用RecyclerView的setAdapter()方法来完成适配器的设置,这样RecyclerViewg和数据之间的关联就建立完成了。
在这里插入图片描述

实现横向滚动和瀑布流布局

把前面自定义的元素布局改成垂直排列;在MainActivity里面加入一行代码:调用LinearLayout的setOrientation()方法来设置布局的排列方向(默认是纵向排列的),传入LinearLayout.HORIZONTAL表示让布局横向排列;
ListView的布局排列是由自身去管理的,RecyclerView将这个工作交给了LayoutManager,LayoutManager里面定制了一套可扩展的布局排列接口。除了LinearLayoutManager还有GridLayoutManager(网格布局)和StaggeredGridLayoutManager(瀑布流布局)
在onCreate()方法里面,创建一个StaaggerdeGiridLayoutManager的实例,StaaggeredGrildLayoutManager的构造函数接收两个参数,第一个参数用于指定布局的列数,第二个参数用于指定布局的排列方向,传入StaggeredGridLayoutManager.VERTICAL表示让布局纵向排列;然后再把创建好的实例设置到RecyclerView中就好了。(因为瀑布流的布局需要各个子项的高度1不一致才可以看到效果,所以可以使用gerRandomLengthName()这个方法,使用random对象来创造一个1到20之间的随机数)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

RecyclerView的点击事件

RecyclerView没有提供类似于setOnItemClickListener()这样的组测监听的方法,所以需要自己给子项具体的View注册点击事件;
先修改ViewHolder,在ViewHolder里面添加变量来保存子项最外层布局的实例,然后再onCreateViewHolder()方法里面注册事件就好了
在这里插入图片描述
在这里插入图片描述

实例练习(聊天界面)

https://github.com/yyyyy-yu/UI

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ListViewRecyclerView 都是 Android 开发中用于展示列表数据的视图组件,它们在功能上相似但有一些关键的区别: 1. **性能优化**: - ListView 在早期版本的 Android 中被广泛使用,但它在处理大量数据或滚动时性能较差,因为它是基于 AbsListView 实现的,内存管理相对较低效。 - RecyclerView 是 Android Lollipop(API 21)之后引入的,采用的是更先进的数据集适配器和视图缓存机制,能更好地处理大规模数据并实现流畅的滚动。 2. **布局管理器**: - ListView 使用的是 ListView.LayoutParams,而 RecyclerView 利用了其内置的 LayoutManager API,如 LinearLayoutManager、GridLayoutManager 等,提供了更灵活的布局方式。 3. **分页和加载更多**: - ListView 不支持直接的分页和滚动到顶部/底部加载更多功能。若要实现,开发者需要自定义滚动监听等。 - RecyclerView 内置了 CoordinatorLayout 的能力,可以轻松地配合 CoordinatorLayout 实现常见的加载更多效果,以及与 AppBarLayout 的协同。 4. **适应性**: - ListView 对于高度固定的布局(如 ListAdapter)表现较好,但不太适合自定义布局或瀑布流样式的数据展示。 - RecyclerView 支持多种视图类型(itemAnimator、ViewHolder等),可以提供更丰富的视觉效果和交互体验。 5. **扩展性和可维护性**: - RecyclerView 提供了更多的扩展点,使得定制化更加方便,且社区资源丰富,易于找到解决方案。 - ListView 的扩展性相对较弱,如果要进行深度定制可能需要更多的代码量。 相关问题: 1. ListViewRecyclerView 的哪个更适合处理大数据量? 2. 如何在 RecyclerView 中实现滚动到顶部的加载更多功能? 3. 如何利用 RecyclerView 的 LayoutManager 来创建瀑布流布局?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值