这篇文章,主要介绍Android中的基本UI控件,包含:ListView、RecyclerView、ViewPager。
目录
ListView是【Android】包下提供的一个列表控件,ListView类似于HTML里面的【li】标签,ListView控件里面可以包含其他的控件,ListView是一个容器,它里面可以包含的是每一项【item】列表项。
如何使用ListView组件呢???我们要使用ListView组件,需要自定义相应的Adapter,然后在Adapter里面渲染相应的item项,下面我们来看下ListView具体使用。
- <?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="match_parent"
- android:orientation="vertical">
-
- <!-- 定义 ListView 组件 -->
- <ListView
- android:id="@+id/lv01"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- </LinearLayout>
- <?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="match_parent"
- android:orientation="vertical">
-
- <!-- 创建列表项 -->
- <TextView
- android:id="@+id/tv01"
- android:textSize="30sp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- </LinearLayout>
我们自定义Adapter的时候,需要继承自【BaseAdapter】类,然后重写里面【4】个方法,每个方法的作用看代码注释。
- package com.android.app2.adapter;
-
- import android.content.Context;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import android.widget.TextView;
-
- import com.android.app2.R;
- import com.android.app2.pojo.User;
-
- import java.util.List;
-
- public class MyListViewAdapter extends BaseAdapter {
-
- // 需要渲染的数据集合
- private List<User> data;
- // 上下文对象
- private Context context;
-
- public MyListViewAdapter(List<User> data, Context context) {
- this.data = data;
- this.context = context;
- }
-
- /**
- * 这个方法就是获取需要渲染的item个数
- * 即:ListView可以显示多少条数据
- */
- @Override
- public int getCount() {
- return data.size();
- }
-
- // 根据下标获取到某个item项
- @Override
- public Object getItem(int position) {
- return data.get(position);
- }
-
- // 获取item对应的id
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- /**
- * 这个就是渲染之后的item组件
- * 每次渲染一条数据就会调用一次getView方法
- * @param position 第几个item
- * @param convertView 保存item项的容器组件
- * @param parent 父容器组件
- * @return
- */
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // 获取保存item项的容器组件
- if (convertView == null) {
- // 判空处理, 避免重复创建对象, 可以提高渲染效率
- convertView = LayoutInflater.from(context).inflate(R.layout.activity_item, parent, false);
- }
- // 获取具体的列表项容器
- TextView textView = convertView.findViewById(R.id.tv01);
- // 将数据设置到 TextView 组件里面
- User user = data.get(position);
- textView.setText(user.getId()+ "----->" + user.getName());
- return convertView;
- }
- }
在【MainActivity】类里面,创建需要渲染的数据集合,并且获取ListView组件对象,将数据传递到ListView组件里面进行渲染。
- package com.android.app2;
-
- import androidx.appcompat.app.AppCompatActivity;
-
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.widget.AdapterView;
- import android.widget.ListView;
-
- import com.android.app2.adapter.MyListViewAdapter;
- import com.android.app2.pojo.User;
-
- import java.util.ArrayList;
- import java.util.List;
-
- public class MainActivity extends AppCompatActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- // 创建数据集合
- List<User> list = new ArrayList<>();
- for (int i = 0; i < 50; i++) {
- list.add(new User(1001 + i, "列表项" + (i + 1001)));
- }
- // 获取 ListView 组件, 传递数据进行渲染
- ListView listView = findViewById(R.id.lv01);
- // 设置 Adapter
- listView.setAdapter(new MyListViewAdapter(list, this));
- }
- }
这个时候,我们其实就可以启动应用,查看效果了,如下所示。
我们可以给每个列表项设置点击事件,只需要通过【setOnItemClickListener】监听器,监听每一个item的点击操作即可。
- public class MainActivity extends AppCompatActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- // 创建数据集合
- List<User> list = new ArrayList<>();
- for (int i = 0; i < 50; i++) {
- list.add(new User(1001 + i, "列表项" + (i + 1001)));
- }
- // 获取 ListView 组件, 传递数据进行渲染
- ListView listView = findViewById(R.id.lv01);
- // 设置 Adapter
- listView.setAdapter(new MyListViewAdapter(list, this));
-
- // 设置点击事件
- listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Log.i("ListView", "点击了第" + position + "个item项.");
- }
- });
- }
- }
再次启动应用,点击某一项,查看控制输出日志。
通过点击事件,我们就可以获取到点击的那一项item,之后就可以进行更多的处理逻辑之类的。
在我们自定义的Adapter类里面,我们在调用【getView】方法的时候,里面写了【findViewById】方法来获取对应的容器组件,没渲染一次,都会调用一次【findViewById】方法,其实【findViewById】方法调用一次就可以了,没必要每次都调用,为了解决这个多次调用的问题,我们可以进行如下代码优化。
- /**
- * 这个就是渲染之后的item组件
- * 每次渲染一条数据就会调用一次getView方法
- * @param position 第几个item
- * @param convertView 保存item项的容器组件
- * @param parent 父容器组件
- * @return
- */
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // 定义 ViewHolder
- ViewHolder viewHolder;
- // 获取保存item项的容器组件
- if (convertView == null) {
- // 初始化 ViewHolder 类
- viewHolder = new ViewHolder();
- // 判空处理, 避免重复创建对象, 可以提高渲染效率
- convertView = LayoutInflater.from(context).inflate(R.layout.activity_item, parent, false);
- // 获取 TextView 组件
- // 获取具体的列表项容器
- viewHolder.textView = convertView.findViewById(R.id.tv01);
- // 将 ViewHolder 设置到当前View里面
- convertView.setTag(viewHolder);
- } else {
- // 已经创建了, 直接从view里获取
- viewHolder = (ViewHolder) convertView.getTag();
- }
- // 将数据设置到 TextView 组件里面
- User user = data.get(position);
- viewHolder.textView.setText(user.getId()+ "----->" + user.getName());
- Log.i("ListView", "渲染item第" + position + "项.");
- return convertView;
- }
-
- // 创建自定义的 ViewHolder 类
- private final class ViewHolder {
- TextView textView;
- }
通过上面的代码优化之后,可以使得每次渲染ListView的时候,减少【findViewById】方法的调用,从而提高运行效率。因为调用【findViewById】方法会去解析【res】目录下的资源文件,然后将其作为每个对象返回,可以看到,【res】目录下的资源文件几乎都是不变的,所以一般情况下只需要解析一次就可以使用,没必须要每次都去解析。
以上,就是ListView组件的相关使用。
RecyclerView不是【Android】包下的组件,我们如果要使用RecyclerView组件,还需要额外的引入这个依赖。RecyclerView组件比ListView组件更加的灵活,RecyclerView允许我们自定义设置布局方式。
RecyclerView是【Androidx】包下面的一个组件,需要引入如下依赖。
- dependencies {
- implementation 'androidx.recyclerview:recyclerview:1.2.1'
- }
RecyclerView对应的版本自己选择即可,Android Studio一般都会有提示,直接选择对应版本即可。
点击同步之后,这个时候Gradle就会根据配置文件去下载对应的依赖。
和前面介绍的ListView组件一样的,我们都需要自定义Adapter类,然后在Adapter类里面实现我们组件数据的渲染。不同的是,ListView需要我们自己去优化渲染,而RecyclerView则替我们封装好了优化渲染的判断逻辑,我们只需要实现相应的类即可。
- package com.android.app2.adapter;
-
- import android.content.Context;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.TextView;
-
- import androidx.annotation.NonNull;
- import androidx.recyclerview.widget.RecyclerView;
-
- import com.android.app2.R;
- import com.android.app2.pojo.User;
-
- import java.util.List;
-
- public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {
-
- /** 需要渲染的数据集合 */
- private List<User> data;
- /** 上下文对象 */
- private Context context;
-
- public MyRecyclerViewAdapter(List<User> data, Context context) {
- this.data = data;
- this.context = context;
- }
-
- /**
- * 创建相应的 ViewHolder 对象
- * @param parent
- * @param viewType
- * @return
- */
- @NonNull
- @Override
- public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- // 获取对应的 item 组件View对象
- View view = View.inflate(context, R.layout.activity_item, null);
- // 创建 ViewHolder 对象
- return new MyViewHolder(view);
- }
-
- /**
- * 填充渲染item的内容
- * @param holder
- * @param position
- */
- @Override
- public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
- User user = data.get(position);
- // 设置需要渲染的数据内容
- holder.textView.setText(user.getName() + "---->" + user.getId());
- }
-
- /**
- * 需要渲染的数据总数
- * @return
- */
- @Override
- public int getItemCount() {
- return data != null ? data.size() : 0;
- }
-
- public class MyViewHolder extends RecyclerView.ViewHolder {
- private TextView textView;
-
- public MyViewHolder(@NonNull View itemView) {
- super(itemView);
- // 获取组件
- textView = itemView.findViewById(R.id.tv01);
- }
- }
- }
- <?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="match_parent"
- android:orientation="vertical">
-
- <!-- 定义 RecyclerView 组件 -->
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/tv01"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- </LinearLayout>
打开主页面对应的类【MainActivity】,在方法中添加测试数据,然后获取RecyclerView组件对象,设置渲染的数据,自定义布局。(注意:如果没有设置布局,那么运行的时候,看不到效果,只会出现一片空白)
- package com.android.app2;
-
- import androidx.appcompat.app.AppCompatActivity;
- import androidx.recyclerview.widget.RecyclerView;
- import androidx.recyclerview.widget.LinearLayoutManager;
-
- import android.os.Bundle;
- import com.android.app2.adapter.MyRecyclerViewAdapter;
- import com.android.app2.pojo.User;
-
- import java.util.ArrayList;
- import java.util.List;
-
- public class MainActivity extends AppCompatActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- // 创建数据集合
- List<User> list = new ArrayList<>();
- for (int i = 0; i < 50; i++) {
- list.add(new User(1001 + i, "列表项" + (i + 1001)));
- }
-
- // 获取 RecyclerView 对象
- RecyclerView recyclerView = findViewById(R.id.rv01);
- // 设置自定义布局
- LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
- recyclerView.setLayoutManager(linearLayoutManager);
- // 创建自定义的 Adapter
- MyRecyclerViewAdapter adapter = new MyRecyclerViewAdapter(list, this);
- recyclerView.setAdapter(adapter);
- }
- }
启动应用,查看运行效果如下所示。
从上面的运行效果可以看到,RecyclerView显示的样式和ListView还是有点不同的,ListView里面每个item之间都有边框,而RecyclerView则没有。
上面采用的线性布局,我们也可以采用网格布局、瀑布流布局。
- // 设置自定义布局
- GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);
- recyclerView.setLayoutManager(gridLayoutManager);
效果如下所示:
- // 设置自定义布局
- StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, LinearLayout.VERTICAL);
- recyclerView.setLayoutManager(layoutManager);
运行效果如下所示:
可以看到采用网格布局和瀑布流布局显示的效果怎么是一样的呢???其实是不一样,这里显示一样的原因是因为我们的数据太有规律了,子树都是一样长,导致两种布局看起来就是一样的效果。
当我们的数据内容不一样的时候,这个时候就可以看到两种布局的区别了,我们修改一下数据来看看两种布局的区别。
以上,就是网格布局和瀑布流布局的差别。
RecyclerView组件没有提供类似于ListView的【setOnItemClickListener】方法,我们需要自己去实现监听方法,一般我们会按照下面的方式进行监听方法的设置。
- public class MyViewHolder extends RecyclerView.ViewHolder {
- private final TextView textView;
-
- public MyViewHolder(@NonNull View itemView) {
- super(itemView);
- // 获取组件
- textView = itemView.findViewById(R.id.tv01);
- // TODO 在这里通过 itemView 的点击事件,去回调自定义的点击事件
- itemView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO 这里回调自定义的点击事件
- if (itemClickListener != null) {
- // getAdapterPosition()方法已经过时了,建议我们去调用
- // itemClickListener.onRecyclerItemClick(getAdapterPosition());
- itemClickListener.onRecyclerItemClick(getAbsoluteAdapterPosition());
- }
- }
- });
- }
- }
-
- /** 创建自定义的监听对象 */
- private OnRecyclerItemClickListener itemClickListener;
-
- /** 这个就是提供给外部进行设置监听对象的 */
- public void setRecyclerItemClickListener(OnRecyclerItemClickListener listener) {
- this.itemClickListener = listener;
- }
-
- /** 创建自定义监听接口 */
- public interface OnRecyclerItemClickListener {
- void onRecyclerItemClick(int position);
- }
- MyRecyclerViewAdapter adapter = new MyRecyclerViewAdapter(list, this);
- recyclerView.setAdapter(adapter);
- // 通过自定义的Adapter,设置监听点击事件
- adapter.setRecyclerItemClickListener(new MyRecyclerViewAdapter.OnRecyclerItemClickListener() {
- @Override
- public void onRecyclerItemClick(int position) {
- // TODO 这个就是我们自定义的点击事件
- Log.i("RecyclerView", "点击了第" + position + "项.");
- }
- });
以上,就是RecyclerView组件的基本使用。
ViewPager组件是一个可以实现页面左右切换效果的组件,它是androidx包里面的组件。下面看下ViewPager组件的具体使用。
ViewPager是一个页面切换的组件,所以这里我们创建三个活动页面,用于测试。
- <?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="match_parent"
- android:orientation="vertical"
- android:background="#FFFFFF00">
-
- <!-- 创建列表项 -->
- <TextView
- android:id="@+id/tv01"
- android:textSize="30sp"
- android:text="第一个页面"
- android:textColor="@color/black"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- </LinearLayout>
- <?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="match_parent"
- android:orientation="vertical"
- android:background="#FF00FF00">
-
- <!-- 创建列表项 -->
- <TextView
- android:id="@+id/tv01"
- android:textSize="30sp"
- android:text="第二个页面"
- android:textColor="@color/black"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- </LinearLayout>
- <?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="match_parent"
- android:orientation="vertical"
- android:background="#FF00AAFF">
-
- <!-- 创建列表项 -->
- <TextView
- android:id="@+id/tv01"
- android:textSize="30sp"
- android:text="第三个页面"
- android:textColor="@color/black"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- </LinearLayout>
在主页面的布局文件里面添加ViewPager组件。
- <?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="match_parent"
- android:orientation="vertical">
-
- <!-- 定义 ViewPager 组件 -->
- <androidx.viewpager.widget.ViewPager
- android:id="@+id/vp01"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- </LinearLayout>
ViewPager组件需要自定义一个Adapter类,然后继承自【PagerAdapter】类,重写里面的方法,然后渲染每个页面。
- package com.android.app2.adapter;
-
- import android.view.View;
- import android.view.ViewGroup;
-
- import androidx.annotation.NonNull;
- import androidx.viewpager.widget.PagerAdapter;
-
- import java.util.List;
-
- public class MyViewPagerAdapter extends PagerAdapter {
-
- /** 有多少个页面 */
- private List<View> data;
-
- public MyViewPagerAdapter(List<View> data) {
- this.data = data;
- }
-
- /**
- * 获取有多少个页面
- * @return
- */
- @Override
- public int getCount() {
- return data.size();
- }
-
- /**
- * 将给定的 position 组件添加到container容器里面,创建并显示
- * 返回一个创建后的view的key值
- * @param container
- * @param position
- * @return
- */
- @NonNull
- @Override
- public Object instantiateItem(@NonNull ViewGroup container, int position) {
- // 添加View到容器
- container.addView(data.get(position), 0);
- return data.get(position);
- }
-
- /**
- * 判断 instantiateItem 方法返回的 view 和当前view是否同一个
- * @param view
- * @param object
- * @return
- */
- @Override
- public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
- return view == object;
- }
-
- /**
- * 销毁view
- * @param container
- * @param position
- * @param object
- */
- @Override
- public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
- // 移除某个View
- container.removeView(data.get(position));
- }
- }
在【MainActivity】主页面类里面,我们准备需要显示的页面集合,然后通过自定义的Adapter进行页面布局的渲染。
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- // 获取 ViewPager 组件
- ViewPager viewPager = findViewById(R.id.vp01);
- // 创建数据
- List<View> data = new ArrayList<>();
- // 获取三个布局对象
- LayoutInflater lf = getLayoutInflater().from(this);
- View page01 = lf.inflate(R.layout.activity_page01, null);
- View page02 = lf.inflate(R.layout.activity_page02, null);
- View page03 = lf.inflate(R.layout.activity_page03, null);
- data.add(page01);
- data.add(page02);
- data.add(page03);
- // 创建自定义Adapter对象
- MyViewPagerAdapter adapter = new MyViewPagerAdapter(data);
- // 渲染
- viewPager.setAdapter(adapter);
- }
启动应用,查看ViewPager效果,左右滑动页面,可以发现页面来回切换。
以上,就是ViewPager组件的相关使用。
综上,这篇文章结束了,主要介绍了Android中的基本UI控件,包含:ListView、RecyclerView、ViewPager。