【Android开发—电商系列】(一):ListView,就这么美
最后更新于:2022-04-01 10:02:16
### 导读
在本篇文章中,你将学到:
- 如何实现广告位和列表的整体下拉刷新。
- ListView的两层嵌套。
- 如何让ListView中的一行显示多个Item。
-
### 先睹为快
项目的首页主要分为两部分:
1. 上部的轮播广告位
1. 下部的商品展示区,在每个商品类别(如,美食,服装,办公用品)下,最多展示6个商品。
> 效果图:
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d234137.jpg "")
不同的分类:
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d28117f.jpg "")
### 布局思路
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d2bf0a3.jpg "")
描述:每个ListView都由HeadView、主体和FooterView组成。
整体:PullToRefreshListView
广告位:PullToRefreshListView的HeaderView
商品展示区:PullToRefreshListView的主体
每个商品大类(如美食类):PullToRefreshListView的一个Item
每个大类下的六个商品:ExpandedListView
每个商品:ExpandedListView的Item
### 实现
### 布局文件
首页布局:active_main.xml
~~~
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_weight="1"
tools:context=".MainActivity" >
<!-- 顶部搜索框 -->
<LinearLayout
android:id="@+id/main_top_layout"
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_alignParentTop="true"
android:background="@color/logoColor"
android:focusableInTouchMode="true"
android:gravity="center_horizontal"
android:orientation="horizontal" >
<ImageView
android:id="@+id/main_top_logo"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:src="@drawable/main_top_logo" />
<EditText
android:id="@+id/main_search_edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="@drawable/bg_searchbox"
android:focusable="false"
android:focusableInTouchMode="true"
android:hint="查询商品"
android:padding="6dp"
android:textColor="@color/darkgray"
android:textSize="12sp"
android:windowSoftInputMode="stateVisible|adjustPan" />
<ImageView
android:id="@+id/index_search_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:paddingLeft="10dp"
android:paddingRight="10dp" />
</LinearLayout>
<!-- 广告位和商品分类列表(整体都在一个下拉刷新的ListView当中) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="45dp">
<jczb.shoping.widget.PullToRefreshListView
android:id="@+id/product_floor_listview"
style="@style/widget_listview"
android:scrollbars="none" />
</LinearLayout>
</RelativeLayout>
~~~
HeaderView布局:main_advertisement_header.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="wrap_content"
android:orientation="vertical"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="136dp"
android:background="#00FFFFFF">
<!-- 广告位中可以左右滑动的控件 -->
<android.support.v4.view.ViewPager
android:id="@+id/adv_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</android.support.v4.view.ViewPager>
</LinearLayout>
<!-- 广告位中的小圆点 -->
<LinearLayout
android:id="@+id/viewGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="115dp"
android:layout_marginRight="10dp"
android:gravity="right"
android:orientation="horizontal"
>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
~~~
商品大类Item布局:main_lv_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:orientation="vertical"
android:background="#FFFFFF">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
>
<!-- 商品分类名称 -->
<TextView
android:id="@+id/tv_category_name"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="美食"
android:layout_alignParentLeft="true"
android:layout_marginLeft="10dp"
android:layout_marginTop="7dp"
android:textSize="20dp"
/>
<ImageView
android:id="@+id/iv_category_color"
android:layout_width="7dp"
android:layout_height="match_parent"
android:background="#55D155"/>
<!--更多箭头 -->
<ImageView
android:id="@+id/iv_product_more"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_alignParentRight="true"
android:layout_marginTop="14dp"
android:layout_marginRight="5dp"
android:layout_marginLeft="3dp"
android:src="@drawable/tb_icon_actionbar_back1" />
<!--更多文本 -->
<TextView
android:id="@+id/tv_product_more"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingRight="8dp"
android:layout_toLeftOf="@id/iv_product_more"
android:layout_marginTop="10dp"
android:text="更多"
android:textSize="16dp"
/>
</RelativeLayout>
<!-- 分割线 -->
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#C0C0C0"
/>
<!--商品大类 -->
<jczb.shoping.adapter.ExpandedListView
android:id="@+id/elv_main_category"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#FFFFFFFF"
android:listSelector="#00000000" >
</jczb.shoping.adapter.ExpandedListView>
<!-- 分割线 -->
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#C0C0C0"
/>
<!-- 分割线 -->
<View
android:layout_width="match_parent"
android:layout_height="8dp"
android:background="#F6F9FE"
/>
</LinearLayout>
~~~
每个商品的Item布局:main_lv_item_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:orientation="horizontal" >
<!--每行展示 2个商品-->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="250dp"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:layout_marginTop="0dp"
android:layout_marginBottom="0dp"
android:orientation="horizontal" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" >
<ImageView
android:id="@+id/iv_main_product_1"
android:layout_width="match_parent"
android:layout_height="170dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:background="#FFFFFF"
android:scaleType="fitXY"
/>
<TextView
android:id="@+id/tv_main_productName_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/iv_main_product_1"
android:layout_alignLeft="@id/iv_main_product_1"
android:layout_marginBottom="5dp"
android:layout_marginLeft="5dp"
android:textSize="13dp"
android:lines="2"
android:ellipsize="end"
/>
<TextView
android:id="@+id/tv_main_productPrice_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_main_productName_1"
android:layout_marginLeft="5dp"
android:textColor="#E2572D"
android:textSize="14dp"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="2dp"
android:layout_height="fill_parent"
android:background="#FFFFFF"
>
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" >
<ImageView
android:id="@+id/iv_main_product_2"
android:layout_width="match_parent"
android:layout_height="170dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:background="#FFFFFF"
android:scaleType="fitXY" />
<TextView
android:id="@+id/tv_main_productName_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/iv_main_product_2"
android:layout_marginLeft="5dp"
android:textSize="13dp"
android:lines="2"
android:ellipsize="end"
android:layout_marginBottom="5dp"
/>
<TextView
android:id="@+id/tv_main_productPrice_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_main_productName_2"
android:layout_marginLeft="5dp"
android:textColor="#E2572D"
android:textSize="14dp"/>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
~~~
### ListView
- PullToRefreshListView(此处省略)
- ExpandedListView
~~~
package jczb.shoping.adapter;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.widget.ListView;
/**
* 用于ListView的嵌套,这个ListView当中可以嵌套ListView
* @author xuchenyang
* 2015年11月9日21:43:01
*/
public class ExpandedListView extends ListView{
public ExpandedListView(Context context) {
super(context);
setBackgroundColor(Color.argb(100, 0, 55, 55));
}
public ExpandedListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public ExpandedListView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
~~~
### Adapter
- PullToRefreshListView的Adapter
~~~
product_floor_listview.setAdapter(new BaseAdapter() {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View inflate = getLayoutInflater().inflate(R.layout.main_lv_item, null);
/*设置商品分类名称*/
//获得显示商品分类的控件
TextView tvCategoryName=(TextView)inflate.findViewById(R.id.tv_category_name);
//从数据集合中获得分类名称
String strCategoryName=lstHomePage.get(position).getName();
//设置控件文本
tvCategoryName.setText(strCategoryName);
/*设置商品分类名称的颜色*/
ImageView ivCategoryColor=(ImageView)inflate.findViewById(R.id.iv_category_color);
//黄,绿,红,蓝
String[] colorValues={"#FFFF77","#55D155","#DB3031","#6278D9"};
int colorIndex=position % 4;
int colorValue=Color.parseColor(colorValues[colorIndex]);
ivCategoryColor.setBackgroundColor(colorValue);
/*设置每个分类下的商品*/
List<Product> lstProduct=lstHomePage.get(position).getProduct();
ListView listView = (ListView) inflate.findViewById(R.id.elv_main_category);
productAdapter=new MainListViewAdapter(MainActivity.this,lstProduct);
listView.setAdapter(productAdapter);
return inflate;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public Object getItem(int position) {
return lstHomePage.get(position);
}
/**
* 显示的商品类别数
*/
@Override
public int getCount() {
return lstHomePage.size();
}
});
/*添加下拉刷新事件*/
product_floor_listview.setOnRefreshListener(new OnRefreshListener(){
@Override
public void onRefresh() {
// Do work to refresh the list here.
new GetDataTask().execute();
}
});
}
~~~
- MyListViewAdapter
~~~
package jczb.shoping.adapter;
import java.util.List;
import jczb.shoping.common.UIHelper;
import jczb.shoping.model.Product;
import jczb.shoping.model.URLs;
import jczb.shoping.ui.ProductsInfoActivity;
import jczb.shoping.ui.R;
import net.tsz.afinal.FinalBitmap;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
/**
* 首页每个分类下商品展示的ListView的Adapter
* @author xuchenyang
* 2015年11月9日20:11:10
*/
public class MainListViewAdapter extends BaseAdapter {
//某个商品分类下商品集合
private List<Product> list;
private Context context;
private FinalBitmap fb;
public MainListViewAdapter(Context context, List<Product> list) {
this.context = context;
fb = FinalBitmap.create(context);
this.list = list;
}
/**
* 获得记录的行数
*/
@Override
public int getCount() {
// 按每行2个显示,算出共显示的行数
if (list.size() % 2 == 0) {
return list.size() / 2;
}
return list.size() / 2 + 1;
}
/**
* 获得每个条目项
*/
@Override
public Object getItem(int position) {
return list.get(position);
}
/**
* 获得每个条目的Id
*/
@Override
public long getItemId(int position) {
return position;
}
/**
* 获得每个条目的视图
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = View.inflate(context, R.layout.main_lv_item_item, null);
//商品的图片
viewHolder.iv_product1 = (ImageView) convertView
.findViewById(R.id.iv_main_product_1);
viewHolder.iv_product2 = (ImageView) convertView
.findViewById(R.id.iv_main_product_2);
//商品名称
viewHolder.tv_name1=(TextView)convertView.findViewById(R.id.tv_main_productName_1);
viewHolder.tv_name2=(TextView)convertView.findViewById(R.id.tv_main_productName_2);
//商品价格
viewHolder.tv_price1=(TextView)convertView.findViewById(R.id.tv_main_productPrice_1);
viewHolder.tv_price2=(TextView)convertView.findViewById(R.id.tv_main_productPrice_2);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
int position1 = position *2;
int position2 = position * 2 + 1;
Product product1=list.get(position1);
/********每行第一个产品的设置********/
//显示产品图片
String pic1 = URLs.imageUrlPrefix+product1.getPro_img();
fb.display(viewHolder.iv_product1, pic1);
//假数据
//viewHolder.iv_product1.setImageResource(R.drawable.clothing_04);
//显示产品名称
viewHolder.tv_name1.setText(product1.getPro_name());
//显示产品价格
viewHolder.tv_price1.setText("¥"+product1.getPro_price());
//把该产品的位置设为图片的tag值,为的是点击图片时能获得它的位置
//viewHolder.iv_product1.setTag(position1);
//把商品的Id保存到Tag中
viewHolder.iv_product1.setTag(product1.getUid());
/***********每行第二个产品的设置*******/
if (position2 < list.size()) {
Product product2=list.get(position2);
String pic2 =URLs.imageUrlPrefix+product2.getPro_img();
fb.display(viewHolder.iv_product2, pic2);
//viewHolder.iv_product2.setImageResource(R.drawable.clothing_0108);
//显示产品名称
viewHolder.tv_name2.setText(product2.getPro_name());
//显示产品价格
viewHolder.tv_price2.setText("¥"+product2.getPro_price());
//viewHolder.iv_product2.setTag(position2);
viewHolder.iv_product2.setTag(product2.getUid());
} else {
//viewHolder.iv_product2.setVisibility(View.GONE);
}
viewHolder.iv_product1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
UIHelper.ToastMessage(context, v.getTag().toString());
//获得产品uid
int uid=Integer.parseInt(v.getTag().toString());
StartProductInfoActivity(uid);
}
});
viewHolder.iv_product2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(v.getTag()!=null){
UIHelper.ToastMessage(context, v.getTag().toString());
//获得产品uid
int uid=Integer.parseInt(v.getTag().toString());
StartProductInfoActivity(uid);
}
}
});
return convertView;
}
/**
* 每个条目项的视图集合
* @author xuchenyang
*
*/
class ViewHolder {
private ImageView iv_product1, iv_product2;
private TextView tv_name1,tv_name2,tv_price1,tv_price2;
}
/**
* 启动商品详情页
* @param uid 商品id
*/
public void StartProductInfoActivity(int uid){
Intent intent=new Intent();
intent.putExtra("uid", uid);
intent.setClass(context, ProductsInfoActivity.class);
context.startActivity(intent);
}
}
~~~
### 总结
- 实现广告位和列表的整体下拉刷新:
将广告位作为ListView的HeaderView。
- ListView的两层嵌套:
嵌套进去的ListView要重新实现OnMeasure方法,否则会报错。
- 如何让ListView中的一行显示多个Item:
在Adapter的getCount方法中进行处理。
基本的ListView我想大家都会实现,但是在这个用户体验度要求很高的Android世界里,怎么能不玩出一点花样呢?ListView,原来可以展现出这么好看的界面,太棒了!有没有豁然开朗的感觉?所以尽情想象吧,我已经陶醉了……
### 附:[下载源码](http://download.csdn.net/detail/u010924834/9270967)