【Android开发—电商系列】(一):ListView,就

导读

在本篇文章中,你将学到:

  • 如何实现广告位和列表的整体下拉刷新。
  • ListView的两层嵌套。
  • 如何让ListView中的一行显示多个Item。

先睹为快

项目的首页主要分为两部分:

  1. 上部的轮播广告位
  2. 下部的商品展示区,在每个商品类别(如,美食,服装,办公用品)下,最多展示6个商品。

效果图:

   这里写图片描述

不同的分类:
   
  这里写图片描述

布局思路

这里写图片描述

描述:每个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,原来可以展现出这么好看的界面,太棒了!有没有豁然开朗的感觉?所以尽情想象吧,我已经陶醉了……

附:下载源码

文章导航