当前位置: 首页 > news >正文

【Android】ListView与RecyclerView的基础使用

【Android】ListView与RecyclerView的基础使用

一、ListView

1. ListView 的基本用法

1.1 在布局中声明 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" ><ListViewandroid:id="@+id/list_view"android:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout>
1.2 准备数据源(通常是一个字符串列表)
String[] data = {"Apple", "Banana", "Orange", "Watermelon","Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango","Apple", "Banana", "Orange", "Watermelon", "Pear", "Grape","Pineapple", "Strawberry", "Cherry", "Mango"};
1.3 创建 Adapter(适配器)
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this,                         // 当前上下文android.R.layout.simple_list_item_1,  // 内置的 item 布局data                          // 数据源
);

数据是无法直接传递给ListView的,我们还需要借助适配器来完成。Android 中提供了很多适配器的实现类,其中 ArrayAdapter 可以通过泛型来指定。

android.R.layout.simple_list_item_1:这是一个预定义的Android系统布局资源ID,用于显示单行文本的列表项。它是一个非常简单的布局,通常用于显示文本内容的列表项。在这里,我们将使用这个布局来显示data数组中的每个图片名称。

1.4 绑定 Adapter 到 ListView
ListView listView = findViewById(R.id.listView);
listView.setAdapter(adapter);

完整代码如下:

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<>(MainActivity.this, android.R.layout.simple_list_item_1, data);ListView listView = (ListView) findViewById(R.id.list_view);listView.setAdapter(adapter);}

运行程序,效果如下,可通过滚动的方式来查看屏幕外的数据:

请添加图片描述

2. 定制 ListView 的界面

2.1 定义一个实体类
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;}
}
2.2 新建一个自定义布局
<?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"><ImageViewandroid:id="@+id/fruit_image"android:layout_width="wrap_content"android:layout_height="wrap_content" /><TextViewandroid:id="@+id/fruit_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginLeft="10dp" /></LinearLayout>

在这个布局中,我们定义了一个 ImageView 用于显示水果的图片,又定义了一个TextView 用于显示水果的名称,并让TextView在垂直方向上居中显示。

2.3 创建一个自定义适配器
public class FruitAdapter extends ArrayAdapter<Fruit> {private int resourceId;public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {super(context, textViewResourceId, objects);resourceId = textViewResourceId;}@NonNull@Overridepublic View getView(int position, @Nullable View convertView, @NonNull 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;}
}

FruitAdapter 重写了父类的一组构造函数,用于将上下文、ListView 子项布局的 id 和数据都传递进来。另外又重写了 getView() 方法,这个方法在每个子项被滚动到屏幕内的时候会被调用。在 getView() 方法中,首先通过 getItem() 方法得到当前项的 Fruit 实例,然后使用 LayoutInflater 来为这个子项加载我们传入的布局。

修改 MainActivity 中的代码如下所示:

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);}private void initFruits() {for (int i = 0; i < 2; i++) {Fruit apple = new Fruit("Apple", R.drawable.apple_pic);fruitList.add(apple);Fruit banana = new Fruit("Banana", R.drawable.banana_pic);fruitList.add(banana);Fruit orange = new Fruit("Orange", R.drawable.orange_pic);fruitList.add(orange);Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);fruitList.add(watermelon);Fruit pear = new Fruit("Pear", R.drawable.pear_pic);fruitList.add(pear);Fruit grape = new Fruit("Grape", R.drawable.grape_pic);fruitList.add(grape);Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);fruitList.add(pineapple);Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);fruitList.add(strawberry);Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);fruitList.add(cherry);Fruit mango = new Fruit("Mango", R.drawable.mango_pic);fruitList.add(mango);}}

这里添加了一个 initFruits() 方法,用于初始化所有水果数据。运行程序,效果如下图所示:
请添加图片描述

3. ListView 的性能优化

目前我们 ListView 的运行效率是很低的,因为在 FruitAdapter的getView() 方法中,每次都将布局重新加载了一遍,当 ListView 快速滚动的时候,这就会成为性能的瓶颈。仔细观察会发现,getview() 方法中还有一个 convertView 参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用。修改FruitAdapter 中的代码,如下所示:

public class FruitAdapter extends ArrayAdapter<Fruit> {private int resourceId;public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {super(context, textViewResourceId, objects);resourceId = textViewResourceId;}@NonNull@Overridepublic View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {Fruit fruit = getItem(position); // 获取当前Fruit实例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() 方法中进行了判断,如果 convertView 为 null ,则使用 LayoutInflater 去加载布局,如果不为 null 则直接对 convertView 进行重用。这样就大大提高了 ListView 的运行效率,在快速滚动的时候也可以表现出更好的性能。

不过,目前我们的这份代码还是可以继续优化的,虽然现在已经不会再重复去加载布局,但是每次在 getView() 方法中还是会调用 View 的 findViewById() 方法来获取一次控件的实例。 我们可以借助一个 ViewHolder 来对这部分性能进行优化,修改 FruitAdapter 中的代码,如下所示:

public class FruitAdapter extends ArrayAdapter<Fruit> {private int resourceId;public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {super(context, textViewResourceId, objects);resourceId = textViewResourceId;}@NonNull@Overridepublic View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {Fruit fruit = getItem(position); // 获取当前Fruit实例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;}
}

这里新增了一个内部类 ViewHolder,用于对控件的实例进行缓存。当 convertView 为 null 的时候,创建一个 ViewHolder 对象,并将控件的实例都存放在ViewHolder 里,然后调用 View 的 setTag() 方法,将 ViewHolder 对象存储在 View 中。当 convertView 不为 null 的时候,则调用 View 的 getTag() 方法,把 ViewHolder 重新取出。这样所有控件的实例都缓存在了 ViewHolder 里,就没有必要每次都通过 findViewById() 方法来获取控件实例了。 通过这两步优化之后,ListView 的运行效率就已经非常不错了。

4. ListView 的点击事件

public class MainActivity extends AppCompatActivity {private List<Fruit> fruitList = new ArrayList<>();@Overrideprotected 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() {@Overridepublic 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();}});}private void initFruits() {for (int i = 0; i < 2; i++) {Fruit apple = new Fruit("Apple", R.drawable.apple_pic);fruitList.add(apple);Fruit banana = new Fruit("Banana", R.drawable.banana_pic);fruitList.add(banana);Fruit orange = new Fruit("Orange", R.drawable.orange_pic);fruitList.add(orange);Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);fruitList.add(watermelon);Fruit pear = new Fruit("Pear", R.drawable.pear_pic);fruitList.add(pear);Fruit grape = new Fruit("Grape", R.drawable.grape_pic);fruitList.add(grape);Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);fruitList.add(pineapple);Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);fruitList.add(strawberry);Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);fruitList.add(cherry);Fruit mango = new Fruit("Mango", R.drawable.mango_pic);fruitList.add(mango);}}
}

可以看到,我们使用 setonItemClickListener() 方法为 ListView 注册了一个监听器,当用户点击了 ListView 中的任何一个子项时,就会回调 onItemClick() 方法。在这个方法中可以通过 position 参数判断出用户点击的是哪一个子项,然后获取到相应的水果,并通过 Toast 将水果的名字显示出来。

运行程序,效果如下:

请添加图片描述

二、RecyclerView

ListView 在 Android 中 仍然可以使用,但在现代开发中 已经不推荐 首选它了。Google 自 Android 5.0(API 21)起推荐使用 RecyclerView 替代 ListView

ListView 的局限性:

  • 没有 ViewHolder 自动复用机制(需要手动写);
  • 缺乏灵活的布局管理(只能垂直排列);
  • 不支持分隔线样式定制、滑动动画、复杂多类型 item;
  • 性能和扩展性较差。

1. RecyclerView 的基本用法

1.1 准备一个适配器
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {private List<Fruit> mFruitList;static class ViewHolder extends RecyclerView.ViewHolder {ImageView fruitImage;TextView fruitName;public ViewHolder(View view) {super(view);fruitImage = (ImageView) view.findViewById(R.id.fruit_image);fruitName = (TextView) view.findViewById(R.id.fruit_name);}}public FruitAdapter(List<Fruit> fruitList) {mFruitList = fruitList;}@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);ViewHolder holder = new ViewHolder(view);return holder;}@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position) {Fruit fruit = mFruitList.get(position);holder.fruitImage.setImageResource(fruit.getImageId());holder.fruitName.setText(fruit.getName());}@Overridepublic int getItemCount() {return mFruitList.size();}
}

虽然这段代码看上去好像有点长,但其实它比 ListView 的适配器要更容易理解。这里我们首先定义了一个内部类 ViewHolder,ViewHolder 要继承自 RecyclerView.ViewHolder。然后 ViewHolder 的构造函数中要传入一个 View 参数,这个参数通常就是 Recycler View子项的最外层布局,那么我们就可以通过 findViewById() 方法来获取到布局中的 ImageView 和 TextView 的实例了。

FruitAdapter 中也有一个构造函数,这个方法用于把要展示的数据源传进来, 并赋值给一个全局变量mFruitList,我们后续的操作都将在这个数据源的基础上进行。

由于 FruitAdapter 继承自 RecyclerView.Adapter,必须重写以下三个核心方法:

  1. onCreateViewHolder():创建并返回一个 ViewHolder 对象,负责加载每个子项的布局。
  2. onBindViewHolder():将数据绑定到 ViewHolder 上,在子项显示时调用,用于设置具体内容。
  3. getItemCount():返回数据项的总数,告诉 RecyclerView 有多少个子项要显示。

这三个方法共同完成了列表项的创建、复用和数据绑定,是实现 RecyclerView 的基础。

1.2 使用这个适配器

public class MainActivity extends AppCompatActivity {private List<Fruit> fruitList = new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initFruits(); // 初始化水果数据RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);LinearLayoutManager layoutManager = new LinearLayoutManager(this);recyclerView.setLayoutManager(layoutManager);FruitAdapter adapter = new FruitAdapter(fruitList);recyclerView.setAdapter(adapter);}private void initFruits() {for (int i = 0; i < 2; i++) {Fruit apple = new Fruit(getRandomLengthName("Apple"), R.drawable.apple_pic);fruitList.add(apple);Fruit banana = new Fruit(getRandomLengthName("Banana"), R.drawable.banana_pic);fruitList.add(banana);Fruit orange = new Fruit(getRandomLengthName("Orange"), R.drawable.orange_pic);fruitList.add(orange);Fruit watermelon = new Fruit(getRandomLengthName("Watermelon"), R.drawable.watermelon_pic);fruitList.add(watermelon);Fruit pear = new Fruit(getRandomLengthName("Pear"), R.drawable.pear_pic);fruitList.add(pear);Fruit grape = new Fruit(getRandomLengthName("Grape"), R.drawable.grape_pic);fruitList.add(grape);Fruit pineapple = new Fruit(getRandomLengthName("Pineapple"), R.drawable.pineapple_pic);fruitList.add(pineapple);Fruit strawberry = new Fruit(getRandomLengthName("Strawberry"), R.drawable.strawberry_pic);fruitList.add(strawberry);Fruit cherry = new Fruit(getRandomLengthName("Cherry"), R.drawable.cherry_pic);fruitList.add(cherry);Fruit mango = new Fruit(getRandomLengthName("Mango"), R.drawable.mango_pic);fruitList.add(mango);}}
}

initFruits() 方法中初始化水果数据。onCreate() 中先获取 RecyclerView 实例,然后创建 LinearLayoutManager 并设置给 RecyclerView,实现类似 ListView 的线性布局。接着创建 FruitAdapter 并传入数据,最后调用 setAdapter() 设置适配器,完成数据与列表的绑定。

运行程序,效果如下:

请添加图片描述

2. 实现横向滚动和瀑布流

2.1 横向滚动

只需加一行代码layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initFruits(); // 初始化水果数据RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);LinearLayoutManager layoutManager = new LinearLayoutManager(this);layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); recyclerView.setLayoutManager(layoutManager);FruitAdapter adapter = new FruitAdapter(fruitList);recyclerView.setAdapter(adapter);
}

效果如下,可横向滚动:

请添加图片描述

2.2 瀑布流暴布局

public class MainActivity extends AppCompatActivity {private List<Fruit> fruitList = new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initFruits(); // 初始化水果数据RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);StaggeredGridLayoutManager layoutManager = newStaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);recyclerView.setLayoutManager(layoutManager);FruitAdapter adapter = new FruitAdapter(fruitList);recyclerView.setAdapter(adapter);}private void initFruits() {for (int i = 0; i < 2; i++) {Fruit apple = new Fruit(getRandomLengthName("Apple"), R.drawable.apple_pic);fruitList.add(apple);Fruit banana = new Fruit(getRandomLengthName("Banana"), R.drawable.banana_pic);fruitList.add(banana);Fruit orange = new Fruit(getRandomLengthName("Orange"), R.drawable.orange_pic);fruitList.add(orange);Fruit watermelon = new Fruit(getRandomLengthName("Watermelon"), R.drawable.watermelon_pic);fruitList.add(watermelon);Fruit pear = new Fruit(getRandomLengthName("Pear"), R.drawable.pear_pic);fruitList.add(pear);Fruit grape = new Fruit(getRandomLengthName("Grape"), R.drawable.grape_pic);fruitList.add(grape);Fruit pineapple = new Fruit(getRandomLengthName("Pineapple"), R.drawable.pineapple_pic);fruitList.add(pineapple);Fruit strawberry = new Fruit(getRandomLengthName("Strawberry"), R.drawable.strawberry_pic);fruitList.add(strawberry);Fruit cherry = new Fruit(getRandomLengthName("Cherry"), R.drawable.cherry_pic);fruitList.add(cherry);Fruit mango = new Fruit(getRandomLengthName("Mango"), R.drawable.mango_pic);fruitList.add(mango);}}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();}
}

构造 StaggeredGridLayoutManager 对象,构造函数的两个参数,第一个参数表示指定布局的列数,第二个参数指定布局的排列方式。

注意 fruit_item.xml 中,LinearLayout 的宽度应该是 macth_parent,因为瀑布流的宽度应该是根据布局的列数自动适配的。

效果如下:

请添加图片描述

3. RecyclerView 的点击事件

RecycleView需要我们为子项具体的View注册点击事件,修改FruitAdapter中代码如下:

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {private List<Fruit> mFruitList;static class ViewHolder extends RecyclerView.ViewHolder {View fruitView;ImageView fruitImage;TextView fruitName;public ViewHolder(View view) {super(view);fruitView = view;fruitImage = (ImageView) view.findViewById(R.id.fruit_image);fruitName = (TextView) view.findViewById(R.id.fruit_name);}}public FruitAdapter(List<Fruit> fruitList) {mFruitList = fruitList;}@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);final ViewHolder holder = new ViewHolder(view);holder.fruitView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {int position = holder.getAdapterPosition();Fruit fruit =mFruitList.get(position);Toast.makeText(v.getContext(), "you clicked view" + fruit.getName(),Toast.LENGTH_SHORT).show();}});holder.fruitImage.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {int position = holder.getAdapterPosition();Fruit fruit =mFruitList.get(position);Toast.makeText(v.getContext(), "you clicked image" + fruit.getName(),Toast.LENGTH_SHORT).show();}});return holder;}@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position) {Fruit fruit = mFruitList.get(position);holder.fruitImage.setImageResource(fruit.getImageId());holder.fruitName.setText(fruit.getName());}@Overridepublic int getItemCount() {return mFruitList.size();}
}

我们先是修改了 ViewHolder,在 ViewHolder 中添加了 fruitView 变量来保存子项最外层布局的实例,然后在 onCreateviewHolder() 方法中注册点击事件就可以了。这里分别为最外层布局和 ImageView 都注册了点击事件,RecyclerView 的强大之处也在这里,它可以轻松实现子项中任意控件或布局的点击事件。我们在两个点击事件中先获取了用户点击的 positio,然后通过 position 拿到相应的 Fruit 实例,再使用 Toast 分别弹出两种不同的内容以示区别。由于 TextView 并没有注册点击事件,因此点击文字这个事件会被子项的最外层布局捕获到。
int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}

@Override
public int getItemCount() {return mFruitList.size();
}

}


我们先是修改了 ViewHolder,在 ViewHolder 中添加了 fruitView 变量来保存子项最外层布局的实例,然后在 onCreateviewHolder() 方法中注册点击事件就可以了。这里分别为最外层布局和 ImageView 都注册了点击事件,RecyclerView 的强大之处也在这里,它可以轻松实现子项中任意控件或布局的点击事件。我们在两个点击事件中先获取了用户点击的 positio,然后通过 position 拿到相应的 Fruit 实例,再使用 Toast 分别弹出两种不同的内容以示区别。由于 TextView 并没有注册点击事件,因此点击文字这个事件会被子项的最外层布局捕获到。
http://www.dtcms.com/a/287131.html

相关文章:

  • 【RK3576】【Android14】Android平台构建
  • MTF算法V1.0
  • Android无需授权直接访问Android/data目录漏洞
  • 【Linux】基本指令
  • 零基础学习性能测试-linux服务器监控:网络iftop
  • 【2025/07/19】GitHub 今日热门项目
  • Libevent(3)之使用教程(2)创建事件
  • Yakit与vps(vps为Linux使用教程)
  • 辛普森悖论
  • SLAM中的非线性优化-2D图优化之激光SLAM基于优化的前端匹配(十八)
  • 2023年CSP入门级第二轮第四题——旅游巴士
  • windows wsl2-06-docker hello world
  • 网络原理——TCP
  • 【学习记录】智能客服小桃(进度更新ing)
  • 张 关于大语言模型(LLM)置信度研究的经典与前沿论文 :温度缩放;语义熵;自一致性;事实与反思;检索增强;黑盒引导;
  • 软考 系统架构设计师系列知识点之杂项集萃(113)
  • LangGraph教程10:LangGraph ReAct应用
  • 基于Electron打包jar成Windows应用程序
  • 技术演进中的开发沉思-39 MFC系列:多重文件和多重视图
  • 安全事件响应分析--基础命令
  • 【52】MFC入门到精通——(CComboBox)下拉框选项顺序与初始化不一致,默认显示项也不一致
  • pytorch:tensorboard和transforms学习
  • HTML5中的自定义属性
  • Jenkins自动化部署.NET应用实战:Docker+私有仓库+SSH远程发布
  • mysql常用总结
  • EMC杂谈-001-基础知识
  • 【面试八股文】软件测试面试题汇总
  • [黑马头条]-项目整合对象存储服务MinIO
  • 百度网盘TV版1.21.0 |支持倍速播放,大屏云看片
  • CS231n-2017 Lecture2图像分类笔记