ListView示例
效果图
代码部分
ListViewActivity(显示ListView的Activity)
package com.gaojc.activity;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import com.gaojc.adapter.MyAdapter;
import com.gaojc.data.MyData;
import com.gaojc.text.R;
import java.util.ArrayList;
public class ListViewActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
ListView myListView = findViewById(R.id.myListView);
// 创建数据源
ArrayList<MyData> myData = new ArrayList<>();
// 适配器类(用来给ListView填充数据)
MyAdapter myAdapter = new MyAdapter(ListViewActivity.this,myData);
/**
* 添加假数据
*/
for (int i = 0; i < 15; i++) {
myAdapter.add(new MyData(0.1 + i + "",10 + i + "%"));
}
// 设置创建生成的数据适配器
myListView.setAdapter(myAdapter);
}
}
相对应的xml文件(activity_list_view.xml)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="com.gaojc.activity.ListViewActivity"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ListView示例"
android:gravity="center"
android:textSize="30dp"/>
<ListView
android:id="@+id/myListView"
android:layout_width="300dp"
android:layout_height="450dp"
android:layout_gravity="center"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="15dp"
android:paddingBottom="20dp"
android:divider="@null"
android:dividerHeight="5dp"
android:background="@drawable/every_day_get_money_bg1"/>
</FrameLayout>
ListView属性介绍
android:divider="@null" :不显示分割线
android:dividerHeight="5dp" :指定分割线的高
android:scrollbars="none" :隐藏滚动条
MyData数据源
package com.gaojc.data;
public class MyData {
public String money; //显示金额
public String ratio; //比例
public MyData(String money, String ratio) {
this.money = money;
this.ratio = ratio;
}
public String getRatio() {
return ratio;
}
}
MyAdapter适配器类
package com.gaojc.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.gaojc.data.MyData;
import com.gaojc.text.R;
import com.gaojc.text.Utils.LogUtil;
import java.util.ArrayList;
/**
* 适配器类(用来给ListView填充数据)
* 需要继承 BaseAdapter
*/
public class MyAdapter extends BaseAdapter {
public Context context;
public ArrayList<MyData> myData;
public MyAdapter(Context context, ArrayList<MyData> myData) {
this.context = context;
this.myData = myData;
}
/**
* @return:要绑定的条目的数目(格子的数量)
*/
@Override
public int getCount() {
return myData.size();
}
/**
* 根据索引(位置)获得该位置的对象
* @param i:索引
* @return:获取该位置的对象
*/
@Override
public MyData getItem(int i) {
// 这里的返回类型改为自己的数据类
return myData.get(i);
}
/**
* @return: 返回条目的id
*/
@Override
public long getItemId(int i) {
return i;
}
/**
* 获取该条目要显示的界面
* @param i:当前条目
* @param convertView:绘制好的视图
* @param viewGroup:对应的xml文件(单个条目 list_view_adapter.xml)
* @return
*/
@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
MyData myData = getItem(i); //获取当前位置的对象
View view;
ViewHolder viewHolder;
if (convertView == null) {
// 把xml布局文件解析成View
view = LayoutInflater.from(context).inflate(R.layout.list_view_adapter, viewGroup, false);
// 添加数据 setTag:监听相对应的事件
viewHolder = new ViewHolder();
viewHolder.textMoney = view.findViewById(R.id.textMoney);
viewHolder.textMoneyTwo = view.findViewById(R.id.textMoneyTwo);
viewHolder.listViewProBar = view.findViewById(R.id.listViewProBar);
viewHolder.ratio = view.findViewById(R.id.ratio);
viewHolder.getButton = view.findViewById(R.id.getButton);
view.setTag(viewHolder);
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag(); //重新获取ViewHolder
}
viewHolder.getButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
LogUtil.e("点击了领取按钮");
// 获取比例
String ratio = myData.getRatio();
LogUtil.e("比例是:" + ratio);
if (ratio.equals("100%")) {
Toast.makeText(context, "可以提现", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "不能提现", Toast.LENGTH_SHORT).show();
}
}
});
// 设置各节点内容
viewHolder.textMoney.setText(myData.money + "元");
viewHolder.textMoneyTwo.setText(myData.money + "元奖励进度");
String ratio = myData.ratio.replace("%", "");
viewHolder.listViewProBar.setProgress(Integer.parseInt(ratio));
viewHolder.ratio.setText(myData.ratio);
return view;
}
// 需要的数据
private class ViewHolder {
TextView textMoney; //红包金额
TextView textMoneyTwo; //金额奖励进度
ProgressBar listViewProBar; //进度比例
TextView ratio; //显示的进度比例数
Button getButton; //领取按钮
}
// 添加数据方法
public void add(MyData data){
if (myData == null){
myData = new ArrayList<>();
}
myData.add(data);
}
}
单个View条目xml(list_view_adapter.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="60dp"
android:background="@drawable/every_day_get_money_listview_item_background">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="2.5"
android:background="@drawable/red_package"/>
<TextView
android:id="@+id/textMoney"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:text="0.3元"
android:background="@drawable/red_package_2"
android:gravity="center"
android:textColor="#FF0000"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
android:orientation="vertical">
<TextView
android:id="@+id/textMoneyTwo"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="0.3元奖励进度"
android:gravity="center"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginBottom="5dp">
<ProgressBar
android:id="@+id/listViewProBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/progress_bg"
android:progress="60"
android:progressDrawable="@drawable/storage_bar" />
<TextView
android:id="@+id/ratio"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="60%"
android:gravity="center"
android:textColor="#FFFFFF"/>
</FrameLayout>
</LinearLayout>
<Button
android:id="@+id/getButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.1"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:background="@drawable/receive"/>
</LinearLayout>
加载进度所需要的进度条(storage_bar.xml)
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 绿色进度条-->
<item
android:id="@android:id/progress">
<scale
android:scaleWidth="100%">
<shape>
<corners android:radius="15dip"/>
<solid android:color="#66CC08"/>
</shape>
</scale>
</item>
</layer-list>
下载项目源码
如何优化ListView?
优化ListView(或者其他类似的列表组件,如RecyclerView等)的性能是移动应用开发中常见的需求,尤其在处理大量数据或复杂项布局时。以下是一些优化ListView性能的策略:
- 复用视图(View Holder模式):通过Adapter的getView()方法(对于RecyclerView则是onCreateViewHolder()和onBindViewHolder())实现视图复用。创建一个视图持有者(View Holder)类来缓存每个列表项的子视图引用,减少每次滚动时查找子视图的开销。
- 异步加载数据和图片:对于网络图片或耗时的数据获取操作,使用异步加载技术,如AsyncTask(Android旧版本,已废弃,推荐使用Kotlin协程、ExecutorService或第三方库如Glide、Picasso等)来避免阻塞UI线程。
- 分页加载:不一次性加载所有数据,而是采用分页加载的方式,只加载当前显示区域及附近的数据,当用户滚动到列表底部时再加载更多数据。
- ViewHolder池技术:在极高的滚动速度下,即使使用了ViewHolder,频繁的创建和销毁ViewHolder也可能成为瓶颈。可以通过实现一个ViewHolder池来重用已经初始化过的ViewHolder对象,进一步减少对象创建和销毁的开销。
- 减少布局层级和优化布局:确保每个列表项的布局尽可能简洁,减少嵌套,使用轻量级的View组件。可以考虑使用、标签优化布局结构。
- 启用硬件加速:如果你的应用目标API级别支持,确保硬件加速功能已开启,这可以提高图形绘制的性能。
- 合理设置ListView的高度:对于ListView,如果内容高度可预知,设置固定高度可以避免ListView在滚动时重复测量子View,从而提升性能。
- 避免过度绘制:使用开发者工具检查并减少界面的过度绘制,确保每个像素点只被绘制一次。
- 使用性能更优的列表组件:对于Android开发,推荐使用RecyclerView替代ListView,RecyclerView提供了更多的灵活性和性能优化选项,比如item动画、ViewType等。
- 监听滑动状态进行适当优化:利用OnScrollListener或RecyclerView.OnScrollListener监听滑动状态,当列表停止滑动时再执行耗时操作,避免影响滑动流畅性。
ListView 和 RecyclerView的区别?
ListView和RecyclerView都是Android中用于展示列表数据的组件,但它们之间存在一些显著的区别:
- 灵活性:RecyclerView提供了更高的灵活性和定制性。它允许你通过不同的LayoutManager(如LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager)来改变列表的布局方式,不仅限于垂直滚动,还可以实现网格视图、瀑布流等布局;而ListView 只支持单一的线性垂直布局。
- 性能优化:RecyclerView相比ListView具有更好的性能。RecyclerView内部实现了ViewHolder模式,这减少了查找View的次数,提高了滚动时的效率。此外,RecyclerView拥有更精细的缓存机制,分为四级缓存(包括mActiveViews、mCachedViews、mAttachedScrap和通过RecycledViewPool实现的跨Adapter缓存),这有助于提高视图重用率,减少内存消耗和CPU使用,使得处理大量数据时表现更流畅。
- 滚动效果与动画:RecyclerView更容易实现复杂的滚动效果和条目动画,因为它提供了更多的回调方法和动画支持;而 ListView 需要手动实现这些功能。ListView 不支持嵌套滚动,而 RecyclerView 支持嵌套滚动。这意味着您可以将 RecyclerView 嵌套在另一个可滚动视图中,例如 ScrollView 或 ViewPager。
- API设计与使用便捷性:RecyclerView 通过使用 RecyclerView.Adapter 和 ViewHolder 的组合来实现视图的重用机制,使得滚动流畅。它会根据视图的可见性动态地创建和重用 ViewHolder,以避免频繁地创建和销毁视图。而 ListView 通过 ArrayAdapter 或 BaseAdapter,没有明确的 ViewHolder 概念。
- 内存管理:RecyclerView的四级缓存系统使其在内存管理上更为高效,尤其是在数据集变化频繁或数据量大时,能更有效地重用和管理视图。
总结来说,RecyclerView 在灵活性、重用机制、动画和交互、性能优化等方面都比 ListView 更强大和灵活。因此,在新的项目中建议使用RecyclerView 替代 ListView。然而,如果仅需简单的线性垂直列表,或者需要与过时的 API 兼容,使用 ListView 也是可以的。