listview是Android开发中最常见的组件之一。在项目开发中,经常需要将listview分组。关于listview分组的原理可以参考android之listview分组实现,简单说就是:将分组的内容和分组的标题按顺序统一放在一个list中,根据list中的内容决定展示分组标题还是分组内容。本文主要讨论分组listview优化的问题,第一部分“listview分组实现”只做一个简单介绍,代码详见:demo下载
一、listview分组实现
- activity_main.xml
<?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/lv_list"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
2. MainActivity.java
public class MainActivity extends AppCompatActivity {
private Context mContext;
private Button btn_compare;
private ListView lv_list;
private List<DataModel> list=null;
private List<DataModel> groupkey=new ArrayList<DataModel>();
private List<DataModel> aList = new ArrayList<DataModel>();
private List<DataModel> bList = new ArrayList<DataModel>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
btn_compare = (Button) findViewById(R.id.btn_compare);
btn_compare.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(mContext, CompareActivity.class);
startActivity(intent);
}
});
lv_list = (ListView) findViewById(R.id.lv_list);
initData();
}
private void initData(){
list = new ArrayList<DataModel>();
DataModel model = new DataModel();
model.setTitle("------1------");
groupkey.add(model);
model = new DataModel();
model.setTitle("------2------");
groupkey.add(model);
for(int i=0; i<8; i++){
model = new DataModel();
model.setContent("上海");
model.setStartTime("20170505"+i);
model.setStartTime("20170505"+(i+10));
aList.add(model);
}
list.add(groupkey.get(0));
list.addAll(aList);
for(int i=0; i<25; i++){
model = new DataModel();
model.setStartTime("20170905"+i);
model.setStartTime("20170905"+(i+10));
model.setContent("科技");
bList.add(model);
}
list.add(groupkey.get(1));
list.addAll(bList);
lv_list.setAdapter(new GroupListAdapter(mContext, groupkey, list));
}
}
3. GroupListAdapter.java
public class GroupListAdapter extends BaseAdapter {
private Context mContext;
private List<DataModel> list_group = new ArrayList<DataModel>();
private List<DataModel> list_event = new ArrayList<DataModel>();
public GroupListAdapter(Context context, List<DataModel> list_group, List<DataModel> list_event){
mContext = context;
this.list_group = list_group;
this.list_event = list_event;
}
@Override
public int getCount() {
return list_event.size();
}
@Override
public DataModel getItem(int position) {
return list_event.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view=convertView;
if(list_group.contains(getItem(position))){
view= LayoutInflater.from(getApplicationContext()).inflate(R.layout.list_group_items, null);
TextView tv_content = (TextView) view.findViewById(R.id.tv_content);
tv_content.setText(getItem(position).getContent());
}else{
view=LayoutInflater.from(getApplicationContext()).inflate(R.layout.list_items, null);
TextView tv_start_time = (TextView) view.findViewById(R.id.tv_start_time);
TextView tv_end_time = (TextView) view.findViewById(R.id.tv_end_time);
TextView tv_content = (TextView) view.findViewById(R.id.tv_content);
tv_start_time.setText(getItem(position).getStartTime());
tv_end_time.setText(getItem(position).getEndTime());
tv_content.setText(getItem(position).getContent());
}
return convertView;
}
@Override
public boolean isEnabled(int position) {
if (list_group.contains(list_event.get(position))){
return false;
}
return super.isEnabled(position);
}
}
4. DataModel.java
public class DataModel {
private String title;
private String startTime;
private String endTime;
private String content;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getStartTime() {
return startTime;
}
public void setStartTime(String startTime) {
this.startTime = startTime;
}
public String getEndTime() {
return endTime;
}
public void setEndTime(String endTime) {
this.endTime = endTime;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
5. list_group_items.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="30dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:layout_gravity="center_vertical"
android:background="#efeff4">
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#f00"
android:textSize="16sp"
android:text="------1------" />
</RelativeLayout>
5. list_items.xml
<?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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/ll_list_items"
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:background="#fff">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_start_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:layout_gravity="right"
android:text="8:00"/>
<TextView
android:id="@+id/tv_end_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textSize="12sp"
android:text="9:00"/>
</LinearLayout>
<View
android:layout_width="2dp"
android:layout_height="match_parent"
android:padding="5dp"
android:background="#f73066d4"/>
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_gravity="center"
android:singleLine="true"
android:textSize="16sp"
android:text="测试数据"
/>
</LinearLayout>
</LinearLayout>
二、listview分组优化
上面的代码并没有对listview优化,listview最常用的优化方法就是使用ViewHolder。我们只需要对GroupListAdapter.java进行修改。
public class GroupListAdapter extends BaseAdapter {
private Context mContext;
private List<DataModel> list_group = new ArrayList<DataModel>();
private List<DataModel> list_event = new ArrayList<DataModel>();
public GroupListAdapter(Context context, List<DataModel> list_group, List<DataModel> list_event){
mContext = context;
this.list_group = list_group;
this.list_event = list_event;
}
@Override
public int getCount() {
return list_event.size();
}
@Override
public DataModel getItem(int position) {
return list_event.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null){
viewHolder = new ViewHolder();
if (list_group.contains(getItem(position))){
convertView = View.inflate(mContext, R.layout.list_group_items, null);
viewHolder.tv_content = (TextView) convertView.findViewById(R.id.tv_content);
}else{
convertView = View.inflate(mContext, R.layout.list_items, null);
viewHolder.tv_start_time = (TextView) convertView.findViewById(R.id.tv_start_time);
viewHolder.tv_end_time = (TextView) convertView.findViewById(R.id.tv_end_time);
viewHolder.tv_content = (TextView) convertView.findViewById(R.id.tv_content);
}
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if(viewHolder.tv_start_time != null) {
viewHolder.tv_start_time.setText(getItem(position).getStartTime());
}
if(viewHolder.tv_end_time != null) {
viewHolder.tv_end_time.setText(getItem(position).getEndTime());
}
viewHolder.tv_content.setText(getItem(position).getTitle());
return convertView;
}
@Override
public boolean isEnabled(int position) {
if (list_group.contains(list_event.get(position))){
return false;
}
return super.isEnabled(position);
}
class ViewHolder{
LinearLayout ll_list_items;
RelativeLayout rl_group_items;
TextView tv_start_time, tv_end_time, tv_content, tv_title;
}
}
虽然对listview性能优化了,但是当快速滑动listview时,会出现错位问题。
三、解决listview分组优化错位问题
为了解决listview分组优化错位问题,我们将分组标题和分组内容item放在同一个item中,判断getItem(position)是否包含在list_group中(即当前getItem(position)是否为分组标题),决定显示分组标题还是分组内容。
我们再次修改GroupListAdapter.java:
public class GroupListAdapter extends BaseAdapter {
private Context mContext;
private List<DataModel> list_group = new ArrayList<DataModel>();
private List<DataModel> list_event = new ArrayList<DataModel>();
public GroupListAdapter(Context context, List<DataModel> list_group, List<DataModel> list_event){
mContext = context;
this.list_group = list_group;
this.list_event = list_event;
}
@Override
public int getCount() {
return list_event.size();
}
@Override
public DataModel getItem(int position) {
return list_event.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null){
viewHolder = new ViewHolder();
convertView = View.inflate(mContext, R.layout.list_items, null);
viewHolder.ll_list_items = (LinearLayout) convertView.findViewById(R.id.ll_list_items);
viewHolder.tv_start_time = (TextView) convertView.findViewById(R.id.tv_start_time);
viewHolder.tv_end_time = (TextView) convertView.findViewById(R.id.tv_end_time);
viewHolder.tv_content = (TextView) convertView.findViewById(R.id.tv_content);
viewHolder.rl_group_items = (RelativeLayout) convertView.findViewById(R.id.rl_group_items);
viewHolder.tv_title = (TextView) convertView.findViewById(R.id.tv_title);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (list_group.contains(getItem(position))){
viewHolder.ll_list_items.setVisibility(View.GONE);
viewHolder.rl_group_items.setVisibility(View.VISIBLE);
viewHolder.tv_title.setText(getItem(position).getTitle());
} else {
viewHolder.ll_list_items.setVisibility(View.VISIBLE);
viewHolder.rl_group_items.setVisibility(View.GONE);
viewHolder.tv_start_time.setText(getItem(position).getStartTime());
viewHolder.tv_end_time.setText(getItem(position).getEndTime());
viewHolder.tv_content.setText(getItem(position).getContent());
}
return convertView;
}
@Override
public boolean isEnabled(int position) {
if (list_group.contains(list_event.get(position))){
return false;
}
return super.isEnabled(position);
}
class ViewHolder{
LinearLayout ll_list_items;
RelativeLayout rl_group_items;
TextView tv_start_time, tv_end_time, tv_content, tv_title;
}
}
这样就能实现listview分组性能优化,并解决分组错位问题。但是无论item是分组标题还是分组内容,都需要同时渲染分组标题和分组内容,只是简单的进行了隐藏,性能并不是最好的。大家如果有更好的方法,可以贴出来讨论。