ListView特性
1. ListView的简单用法
layout代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ListTest">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/list_view"
>
</ListView>
</LinearLayout>
Activity代码:
package com.example.list_test;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class ListTest extends AppCompatActivity {
private String listData[] = new String[100];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_test);
initData();
ArrayAdapter adapter = new ArrayAdapter(getApplicationContext(),android.R.layout.simple_list_item_1,listData);
ListView listView = findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
private void initData(){
for (int i = 0; i <100 ; i++) {
listData[i] = "这是第"+(i+1)+"项";
}
}
}
2. 自定义ListView
通过自定义layout文件,通过LayoutInflater.from(getContext()).inflate(layoutResource, parent,false);将自定义的layout生成一个view,并将view对象放在listview的item中。下面就是一个简单的示例
item layout代码:
<?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"
android:orientation="horizontal"
>
<ImageView
android:id="@+id/list_item_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/list_item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center"
/>
</LinearLayout>
这是MainActivity代码:
package com.example.my_list_view_test;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class MyListViewTest extends AppCompatActivity {
private List<ImageAndText> list = new ArrayList<ImageAndText>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_list_view_test);
initList();
MyAdapter adapter = new MyAdapter(MyListViewTest.this, R.layout.item_layout, list);
ListView listView = findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
private void initList(){
for (int i = 0; i <10000 ; i++) {
ImageAndText imageAndText = new ImageAndText(R.drawable.picture1, "这是第"+(i+1)+"项");
list.add(imageAndText);
}
}
}
这是Adapter代码:
package com.example.my_list_view_test;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
public class MyAdapter extends ArrayAdapter<ImageAndText> {
private int layoutResource;
public MyAdapter(Context context, int resource, List<ImageAndText> objects) {
//这个是父类的构造方法
super(context, resource, objects);
layoutResource = resource;
}
//这个方法是当ListView的item项进入屏幕的时候进行调用的
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//这里是要获取到划入平时是的ImageAndText对象
ImageAndText imageAndText = getItem(position);
View view = LayoutInflater.from(getContext()).inflate(layoutResource, parent,false);
ImageView imageView = view.findViewById(R.id.list_item_image);
TextView textView = view.findViewById(R.id.list_item_text);
imageView.setImageResource(imageAndText.getImageId());
textView.setText(imageAndText.getTextStr());
return view;
}
}
这是对image和textView封装类
package com.example.my_list_view_test;
public class ImageAndText {
int imageId;
String textStr;
public ImageAndText(int imageId,String textStr){
this.imageId = imageId;
this.textStr = textStr;
}
public int getImageId() {
return imageId;
}
public void setImageId(int imageId) {
this.imageId = imageId;
}
public String getTextStr() {
return textStr;
}
public void setTextStr(String textStr) {
this.textStr = textStr;
}
}
这是效果图:
左边是图片右边是文字,这样的列表项是不是很熟悉。没错!他就是我们浏览新闻app时展示新闻的形式、它就是我们微信的聊天列表的形式。这样的一个listview是不是就亲切很多了。们可能会有疑问,要是按照上面的例子写的demo的话,这样的话软件运行迟早会出现内存溢出,而导致软件崩溃。这个确实时这样的,但是聪明的谷歌工程师早就想到了这个问题。接下来我们简单看看人家时怎么解决问题的。
3. ListView简单性能优化
listview性能优化的思路。就是通过减少new使用的次数(也就是通过减少创建对象的次数来达到优化的目的)。在上面的getView()方法中有三个参数。int position, 记录现在listview中的item的位置的。View convertView,这个是用来记录缓存信息,这个参数也是listview复用旧创建对象的关键。ViewGroup parent,这个就是item中的ViewGroup在里边放各种的view。
优化后的Adapter:
package com.example.my_list_view_test_optimize_version;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
public class MyAdapter extends ArrayAdapter<ImageAndText> {
private int layoutResource;
public MyAdapter(Context context, int resource, List<ImageAndText> objects) {
super(context, resource, objects);
layoutResource = resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder viewHolder;
//获取将要滚动进入屏幕的ImageAndText对象
ImageAndText imageAndText = getItem(position);
//使用LayoutInflater中的from()方法获取LayoutInflater对象然后调用inflater方法进行加载布局文件
//converView是用来缓存已经存在的view对象的,要是存在该view对象,那么就直接使用,要是没有该对象的话
//那就创建一个新的对象
if (convertView == null){
view = LayoutInflater.from(getContext()).inflate(layoutResource, parent,false );
TextView textView = view.findViewById(R.id.list_item_text);
ImageView imageView = view.findViewById(R.id.list_item_image);
viewHolder = new ViewHolder(imageView, textView);
view.setTag(viewHolder);
}else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.textView.setText(imageAndText.getTextStr());
viewHolder.imageView.setImageResource(imageAndText.getImageId());
return view;
}
//因为listView当中的所有的视图都是一样的,就是数据有点不一样
//所有为了节省内存new关键字不能用得太多
class ViewHolder{
ImageView imageView;
TextView textView;
private ViewHolder(ImageView imageView,TextView textView){
this.imageView = imageView;
this.textView = textView;
}
}
}
上面的意思大概就是将创建的view对象和控件缓存起来,要使用的时候直接拿出来用,不用再次创建对象或者是重新找控件(这是因为listview中每个item都是一样的,只是数据的不一样,所以将重复的数据利用起来就达到了优化的目的),要是想要深入了解,推荐郭霖大佬的博客:博客链接深入理解ListView的工作原理。
优化前后的性能对比:
测试规则都是通过加载1000项的item看看两个内存的使用情况
这是优化前的
这是优化后的
通过两个的对比,可以很明显的看到两个版本的内存消耗的情况,同样是加载1000项,但是优化版本的内存使用情况是没有优化的1/2,这样的一个优化确实是厉害。当然这个优化只是简单的优化,由于我的知识有限我就不在拓展了,要是有兴趣的话大家可以自行去学习。
RecycleView的特性
RecycleView其实和ListView是很相像的RecycleView可以说是ListView的增强版本。所以这里边有很多的东西都是相通的。
- RecycleView的简单使用
item layout文件:
<?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"
android:orientation="horizontal"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/list_item_image"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/list_item_text"
/>
</LinearLayout>
layout文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
MainActivity文件:
package com.example.recycle_view_test;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private List<ImageAndText> list = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initListData();
MyAdapter adapter = new MyAdapter(list);
RecyclerView recyclerView = findViewById(R.id.recycle_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
}
private void initListData(){
for (int i = 0; i < 10000 ; i++) {
ImageAndText imageAndText = new ImageAndText(R.drawable.picture1,"这是第"+(i+1)+"项");
list.add(imageAndText);
}
}
}
Adapter文件:
package com.example.recycle_view_test;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
/**
* onCreateViewHolder()、onBindViewHolder方法调用的顺序
*
* 1、第一次的时候:当第一次加载时候没有ViewHolder的时候系统会自己创建一个ViewHolder对象
* 然后ViewHolder对象将会被存放到Recycle存起来(这里了解不是很如就暂时这样理解先)
*
* 2、第二次,也就是加载第二屏信息的时候:将Recycle会自动在缓存中查找否是已经存在ViewHolder
* 要是存在的话就会直接使用该ViewHolder中的对象
*
*/
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
private List<ImageAndText> listData;
//适配器的构造方法
public MyAdapter(List<ImageAndText> listData){
this.listData = listData;
}
//这个方法是用来创建一个ViewHolder对象的
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
//这个方法是用来对滚入屏幕的view进行数据赋值的
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.textView.setText(listData.get(position).getTextStr());
holder.imageView.setImageResource(listData.get(position).imageId);
}
//这个方法是用来告诉RecycleView的数据长度书多少,直接返回数据长度就可以了
@Override
public int getItemCount() {
return listData.size();
}
static class ViewHolder extends RecyclerView.ViewHolder{
ImageView imageView;
TextView textView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.list_item_image);
textView = itemView.findViewById(R.id.list_item_text);
}
}
}
image和textView封装类:
package com.example.recycle_view_test;
public class ImageAndText {
int imageId;
String textStr;
public ImageAndText(int imageId, String textStr){
this.imageId = imageId;
this.textStr = textStr;
}
public int getImageId() {
return imageId;
}
public void setImageId(int imageId) {
this.imageId = imageId;
}
public String getTextStr() {
return textStr;
}
public void setTextStr(String textStr) {
this.textStr = textStr;
}
public class ViewHolder {
}
}
这样的实现其实就是和ListView一样的效果。但是通过这样的一个实现的方法,逻辑就清晰了很多。RecycleView中的逻辑都是在Adapter中进行实现的。
- RecycleView简单性能优化
上面已经说了,RecycleView中的逻辑实现都是都是在Adapter中进行实现的,所以RecycleView的性能优化也是在Adapter实现的;同ListView一样,RecycleView的优化的思路也是一样的都是通过复用已创建的对象来进行性能的优化。
上文中的MyAdapter的代码已经给出来了,其实这个就是安卓官方给出的优化建议,现在我们一起来看看这到底是怎么一回事。
我们自定义Adapter的话要继承自RecyclerView.Adapter这个类的,继承了这个类之后我们要实现三个方法,分别是onCreateViewHolder()方法用来创建 - RecycleView中特殊功能
实现横向滚动;在ListViwe中是无法横向进行滚动的。RecycleView通过LinearLayoutManager对象可以对滚动给的方向进行设定。RecycleView默认的滚动的方向是垂直滚动的。但是我们layout的参数进行设定(layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL))滚动的方向是水平滚动。
***实现控件的单一点击:***和ListView不一样的是,RecycleView可以实现单一控件进行点击。在ListView上我们每点击一个item它就会出发相应的事件,现在有一个需求就是在item中的图片,我要点击图片然后看图片的详情,有人会想这样不就是很简单吗?我们点击item的时候直接跳转到图片的详情不就行了吗?但是现在要求又改了,说点击文字的时候能将文字放大显示,这样一来ListView就无法实现了。但是RecycleView就可以实现!RecycleView中的控件都可以单独设置事件,当没控件事件进行响应的时候item就会进行事件捕获从而进行响应。当然RecycleView的强大的功能不止这些,有兴趣的可以自己自行深入研究。
Recycle应用实例:
上面可以对每个控件进行点击,并响应。当然这个RecycleView的应用还体现在聊天记录上。大家自行进行感受
先这样吧!能力有限要是日后在进行详细的完善……