我想要说
今天学习 ListView 组件,和其它的组件不同的是。ListView 组件并不是简简单单的创建和使用就行,ListView 组件中,需要放置其某一个项,也就是 item。在说实例之前,先来看看 ListView 是长什么样子的吧。
由于这个组件非常的重要,会讲的详细一些。
ListView 学好了之后,后面的 GridView 也是大同小异
打码技术堪忧,哈哈哈哈哈哈😅😅😅
这是一张微信中的截图,这个界面可以认为是包含了一个 ListView,这些每一个小项,就叫做 item,比如“雨课堂”就是一个 item。而且,这些 item 中的布局也是需要自己来实现布局的,所以还需要用到一个布局的 xml 文件。布局难度稍微增加了一些,但是不要怕。
通过这张图,对 ListView 有了一个更好的认识。
重点来咯
自己创建 Activity
以下内容涉及到自定义一个布局和与其对应的 Java 代码。
创建布局文件
1、创建一个布局文件
2、设置文件名,名字为 activity_list_view,点击 OK
至此 activity_list_view.xml 文件创建完成。
创建 java 文件
1、创建一个 java 文件,文件名为 ListViewActivity。
2、重写 onCreate 方法,并将 activity_list_view 布局加载进去。
写法如下:
setContentView(R.layout.activity_list_view);
完整代码:
package top.bestguo.androidlayout.listview;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import top.bestguo.androidlayout.R;
/**
* Created by BestGuo on 2021/1/28.
*/
public class ListViewActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
}
}
3、在 AndroidManifest.xml 文件中,添加一个 activity。
至此,Activity 的创建已经完成了
新建一个 ListView 组件
创建 ListView 非常简单,直接创建即可。给它赋予一个 id,后面需要使用到它。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
创建完成之后,右边的预览界面会有一个效果图出来。
这是它展示的一个样例,直接运行我没有去尝试。不知道直接运行能不能看到这样的效果。
自定义 item
自定义 item 是非常重要的,也就是微信截图中“雨课堂”的那部分。本实例中,我们要设置的 item 效果如下
所以,我们还需要另外的一个 xml 文件用来设计 item。
新建布局文件
新建一个布局文件,名字为 layout_list_item.xml 文件
编写布局代码
编写布局代码(代码中的注释需要去看)
<?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:padding="15dp">
<!-- 创建了一个图片组件 -->
<ImageView
android:id="@+id/iv"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:background="@color/colorBlack"/>
<!--
创建了另外一个布局,用于放置图片右边的文字描述。
宽度和父类相同,高度由里面的内容来决定。
设置了 marginTop="4dp"
-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="4dp">
<!--
第一个 TextView 组件,用来展示标题。设置了文字大小和 paddingLeft="15dp"
-->
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="Hello"
android:paddingLeft="15dp"
android:textSize="20sp"
android:textColor="@color/colorBlack"/>
<!-- 第二个 TextView 组件,用来展示时间 -->
<TextView
android:id="@+id/tv_time"
android:paddingLeft="15dp"
android:layout_marginTop="5dp"
android:textSize="15sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorGray"
android:text="2020-1-28"/>
<!-- 第三个 TextView 组件,用来展示简要介绍 -->
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:paddingLeft="15dp"
android:text="这是内容"
android:textColor="@color/colorAccent"
android:textSize="15dp" />
</LinearLayout>
</LinearLayout>
得到的 item 的效果就是如图所示的效果
注意:之前在设置字体颜色和背景颜色,是用16进制来设置的。但是在这里是用
@color/colorAccent
设置的。
其实这个不难设置,只需在 res 文件夹下的 values 文件夹下的 colors.xml 来进行设置的。通过每一个英文所对应的十六进制颜色代码来设置就可以了。后期使用颜色,直接使用@color/xxxxxx
这种写法即可。
新建 Adapter
非常重点的部分来咯!什么是 Adapter,Adapter 是用来在展示这些 item 的。以下的微信截图用红色圈出的部分就是由 Adapter 将这些 item 给创建出来的。
什么?适配器,不要和我讲适配器,我可不懂是什么鬼东西。
咦,我说明的文字这么这么小,太不给力了。😱😱😱
Adapter 有很多种,在学习时说是使用 BaseAdapter 最多,那我们就用 BaseAdapter 来举例。
首先新建一个 MyListAdapter 类,需要继承自 BaseAdapter 类,重写其相关的方法才是真正的运用到。
继承之后,需要重写的方法共有 4 个,分别如下:
/**
* Created by BestGuo on 2021/1/28.
*/
public class MyListAdapter extends BaseAdapter {
/**
* ListView 组件中需要放置的多少项,这个是要从服务端来进行获取的值。
* 后面的示例,由于没有涉及到服务端,展示一下效果,会直接在此设置数值
* @return 整型
*/
@Override
public int getCount() {
return 0;
}
// 通过索引获取内容,但是我们目前没有内容,因此先放一下
@Override
public Object getItem(int i) {
return null;
}
// 获取id,具体是什么样的id还需要看什么场景,因此先放一下
@Override
public long getItemId(int i) {
return 0;
}
/**
* 列表中的样式每一项的设置
*
* @param i ListView 中的索引
* @param view 自定义的 item 布局,也就是对应我们之前创建的 layout_list_item.xml 文件
* @param viewGroup 不知道,到时候用到在介绍
* @return
*/
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
return null;
}
}
中间的两个方法,可以先不管它。暂时用不到
重写方法
我们主要重写的方法有两个,一个 getCount()
,另一个是 getView()
getCount 方法
这个方法的用途很简单,就是要让 ListView 知道要放多少个 item。一般是服务器动态获取的,也可以是人为设置的。
假设我们只要 15 项,且永远不变。那我们的 getCount 方法中的返回值,那就是 15。所以是这样写。
public int getCount() {
return 15;
}
假设我们从服务器获取到的数据,用的是 ArrayList 保存的,如果要知道获取多少条数据。那么就调用它的 size
方法即可。
private ArrayList arr = new ArrayList();
...
public int getCount() {
return arr.size();
}
getView 方法
这个方法的用途是将我们自定义的 item 布局进行处理。对每一个 item 中的组件的值进行一些设置,比如“雨课堂”的图标、雨课堂文字、发布日期。这些就是 item 中的组件,需要对其进行设置。
下图是雨课堂部分(红色圈出的部分就是微信中 item 用到的组件)
以下红色圈圈的部分就是我们需要设置的部分
其中这里面有一个非常重要的方法,就是 inflate,inflate 方法是用于将我们自定义的 layout_list_item.xml 文件自动填充到 View 中,只有填充进去了,才能正确的调用到这个 layout 文件中设置的 id 值。
LayoutInflater 可以看成是一个布局扩充的一个类,将 layout_list_item 自定义的组件扩到 activity_list_view 这个组件中。它有两个方法,一个
from
方法,用于指定 layout_list_item 是 activity_list_view 组件管理的;一个inflate
方法,将 layout_list_item 填充到 activity_list_view 中。
但是,执行完 inflate 方法,并没有将这些组件真正的填充进去,只是给这些组件“创建了一个座位”。要将这些组件保存起来,需要用到 view 中的setTag
方法来将找到的组件真正的保存起来。setTag 传入的参数类型为 Object,因此,需要创建一个类,用于保存这些 item 中的组件。
以下伪代码如下
// 定义一个布局扩充类
private LayoutInflater layoutInflater = LayoutInflater.from(ListViewActivity.this);
...
// 自定义的内部类,用它来将 layout_list_item 中的组件将其保存起来
private static class ViewHolder {
ImageView imageView;
TextView tvClock, tvContent, tvTitle;
}
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
// 第一次调用 getView 方法时。首先这个自定义的组件肯定是没有进行填充进去。
if(view == null){
// 开始扩充“座位”,并将 layout_list_item 保存到变量中。
view = layoutInflater.inflate(R.layout.layout_list_item, null);
// 创建一个自定义的对象,将找到的组件放到对象中
viewHolder = new ViewHolder();
viewHolder.imageView = view.findViewById(R.id.iv); // 找到 ImageView 组件
viewHolder.tvClock = view.findViewById(R.id.tv_time); // 找到 TextView 组件
viewHolder.tvTitle = view.findViewById(R.id.tv_title); // 找到 TextView 组件
viewHolder.tvContent = view.findViewById(R.id.tv_content); // 找到 TextView 组件
// 全部找齐,保存这些 item 中的组件。
view.setTag(viewHolder);
} else {
// 第二次进行调用,此时这个自定义的组件已经是填充进去的,将其获取到。方便设置值
viewHolder = (ViewHolder) view.getTag();
}
// 给控件赋值
viewHolder.tvClock.setText("2021-1-28");
viewHolder.tvTitle.setText("Hello!");
viewHolder.tvContent.setText("我是可爱的祢豆子 n(*≧▽≦*)n");
// 添加网络图片
if(i%2 != 0)
Glide.with(context).load("http://www.topacg.com/wp-content/uploads" +
"/2020/03/frc-11c619718c036bf579c246cdd07e6d77.jpeg").into(viewHolder.imageView);
else
Glide.with(context).load("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl." +
"duitang.com%2Fuploads%2Fitem%2F201912%2F10%2F20191210915" +
"02_8PjP3.thumb.700_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang." +
"com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg").into(viewHolder.imageView);
Log.d("id=", i+"");
return view;
}
完整代码如下
以下代码用到了构造方法,用于在创建该 Adapter 对象时,将当前的 ListViewActivity 给传入进去。
package top.bestguo.androidlayout.listview;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import top.bestguo.androidlayout.R;
/**
* Created by BestGuo on 2021/1/28.
*/
public class MyListAdapter extends BaseAdapter {
// 传入的 Activity,也就是 ListItemActivity
private Context context;
// 获取自己设置的布局
private LayoutInflater layoutInflater;
public MyListAdapter(Context context) {
this.context = context;
this.layoutInflater = LayoutInflater.from(context);
}
/**
* 列表的长度是多少
* @return 整型
*/
@Override
public int getCount() {
return 15;
}
// 通过索引获取内容,但是我们目前没有内容,因此先放一下
@Override
public Object getItem(int i) {
return null;
}
// 获取id,具体是什么样的id还需要看什么场景
@Override
public long getItemId(int i) {
return 0;
}
// 自定义的内部类,用它来将 layout_list_item 中的组件将其保存起来
private static class ViewHolder {
ImageView imageView;
TextView tvClock, tvContent, tvTitle;
}
/**
* 列表中的样式每一项的设置
*
* @param i 每一个 item 中的第几项
* @param view 对应的 item 布局
* @param viewGroup 不知道,到时候用到在介绍
* @return
*/
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
// 第一次调用 getView 方法时。首先这个自定义的组件肯定是没有进行填充进去。
if(view == null){
// 开始扩充“座位”,并将 layout_list_item 保存到变量中。
view = layoutInflater.inflate(R.layout.layout_list_item, null);
// 创建一个自定义的对象,将找到的组件放到对象中
viewHolder = new ViewHolder();
viewHolder.imageView = view.findViewById(R.id.iv); // 找到 ImageView 组件
viewHolder.tvClock = view.findViewById(R.id.tv_time); // 找到 TextView 组件
viewHolder.tvTitle = view.findViewById(R.id.tv_title); // 找到 TextView 组件
viewHolder.tvContent = view.findViewById(R.id.tv_content); // 找到 TextView 组件
// 全部找齐,保存这些 item 中的组件。
view.setTag(viewHolder);
} else {
// 第二次进行调用,此时这个自定义的组件已经是填充进去的,将其获取到。方便设置值
viewHolder = (ViewHolder) view.getTag();
}
// 给控件赋值
viewHolder.tvClock.setText("2021-1-28");
viewHolder.tvTitle.setText("Hello!");
viewHolder.tvContent.setText("我是可爱的祢豆子 n(*≧▽≦*)n");
// 添加网络图片
if(i%2 != 0)
Glide.with(context).load("http://www.topacg.com/wp-content/uploads" +
"/2020/03/frc-11c619718c036bf579c246cdd07e6d77.jpeg").into(viewHolder.imageView);
else
Glide.with(context).load("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl." +
"duitang.com%2Fuploads%2Fitem%2F201912%2F10%2F20191210915" +
"02_8PjP3.thumb.700_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang." +
"com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg").into(viewHolder.imageView);
Log.d("id=", i+"");
return view;
}
}
ListViewActivity.java 文件
package top.bestguo.androidlayout.listview;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import top.bestguo.androidlayout.R;
/**
* Created by BestGuo on 2021/1/28.
*/
public class ListViewActivity extends Activity {
private ListView listView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
listView = findViewById(R.id.list_view);
// 设置一个适配器
listView.setAdapter(new MyListAdapter(this));
}
}
运行效果
运行效果如下
别写了,再写到我脑阔都晕咯🙄🙄🙄
事件
事件这一部分还是较为简单,ListView 共有两种常用的事件,分别是 setOnItemClickListener
和 setOnItemLongClickListener
两个方法。一个是手指短按的事件,另外一个是长按的事件。
案例如下
package top.bestguo.androidlayout.listview;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import top.bestguo.androidlayout.R;
/**
* Created by BestGuo on 2021/1/28.
*/
public class ListViewActivity extends Activity {
private ListView listView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
listView = findViewById(R.id.list_view);
// 设置一个适配器
listView.setAdapter(new MyListAdapter(this));
// 设置点击某一项的监听事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(ListViewActivity.this, "点击的是第" + i + "项!", Toast.LENGTH_SHORT).show();
}
});
// 设置点击某一项长按的监听事件
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(ListViewActivity.this, "长按的是第" + i + "项!", Toast.LENGTH_SHORT).show();
return true;
}
});
}
}
运行效果
总结
总算是吧这根骨头给啃了,可以好好的休息一下了,现在已经凌晨1点多了。
之后的网格布局,我感觉其实都差不多,后面的就好一些吧。
请勿发布违反中国大陆地区法律的言论,请勿人身攻击、谩骂、侮辱和煽动式的语言。