Android高仿美团外卖点菜联动列表效果

最近项目中有一个添加购物车的需求,需要做成美团外卖点菜联动ListView的效果,可能有的朋友觉得这很简单,不就是2个Listview点击事件联动处理机制吗?没错,基本思路就是这样子,只是美团外卖点菜效果上有一种根据右边列表滑动可以监听到左边分类信息的变化状态。

可能言语上表达你们没法想象,先上一张效果图:

完成此效果需要掌握以下知识点:

  • ListView基本使用
  • ListView点击监听事件setOnItemClickListener和setOnScrollListener的了解掌握
  • 简单动画的技能(主要是点击添加复制一个图层进行抛物线落到购物车内)
  • 接口回调的基本使用

不多说了,直接上代码比较靠谱!
以下就是两个ListView最主要的两个监听,来实现美团外卖点菜效果。

//左边ListView的点击事件切换右边列表数据left_listView.setOnItemClickListener(new OnItemClickListener() {    @Override    public void onItemClick(AdapterView<?> arg0, View view,            int position, long arg3) {        isScroll = false;        for (int i = 0; i < left_listView.getChildCount(); i++) {            if (i == position) {                left_listView.getChildAt(i).setBackgroundColor(                        Color.rgb(255, 255, 255));            } else {                left_listView.getChildAt(i).setBackgroundColor(                        Color.TRANSPARENT);            }        }        int rightSection = 0;        for (int i = 0; i < position; i++) {            rightSection += sectionedAdapter.getCountForSection(i) + 1;        }        right_listview.setSelection(rightSection);    }});//右边ListView滑动位置来更新左边ListView分类信息的位置切换right_listview.setOnScrollListener(new OnScrollListener() {    @Override    public void onScrollStateChanged(AbsListView arg0, int arg1) {    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem,            int visibleItemCount, int totalItemCount) {        if (isScroll) {            for (int i = 0; i < left_listView.getChildCount(); i++) {                if (i == sectionedAdapter                        .getSectionForPosition(firstVisibleItem)) {                    left_listView.getChildAt(i).setBackgroundColor(                            Color.rgb(255, 255, 255));                } else {                    left_listView.getChildAt(i).setBackgroundColor(                            Color.TRANSPARENT);                }            }        } else {            isScroll = true;        }    }});

其实代码很简单,看到上述2个ListView的监听事件后就能明白什么原理了。
1.左边ListView通过setOnItemClickListener点击item来切换更新右边列表数据
2.右边ListView通过setOnScrollListener滑动item来更新左边ListView列表
3.右边ListView的适配器需要大家注意一下:

下面主要实现购物车增加、删减商品功能

  • 自己定义2个接口onCallBackListener和ShopToDetailListener
  • onCallBackListener接口主要用于用户操作商品列表时增加和删减商品后对购物车商品价格以及数量的更新
    /**     * Type表示添加或减少     * @param product     * @param type     */    void updateProduct(ShopProduct product, String type);
  • ShopToDetailListener接口主要用于用户操作购物车商品时,对商品列表进行更新和购物车商品数目为0时更新删除购物车该商品信息
    /**     * Type表示添加或减少     * @param product     * @param type     */    void onUpdateDetailList(ShopProduct product, String type);    /**     * 删除购物车商品信息     * @param product     */    void onRemovePriduct(ShopProduct product);

下面操作是界面右边ListView适配器Adapter中增加和减少商品的点击事件处理:

viewHolder.increase.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                int num = product.getNumber();                num++;                product.setNumber(num);                viewHolder.shoppingNum.setText(product.getNumber()+"");                if (callBackListener != null) {                    callBackListener.updateProduct(product, "1");                } else {                }                if(mHolderClickListener!=null){                    int[] start_location = new int[2];                    viewHolder.shoppingNum.getLocationInWindow(start_location);                    //获取点击商品图片的位置                    //复制一个新的商品图标                    //TODO:解决方案,先监听到左边ListView的Item中,然后在开始动画添加                    mHolderClickListener.onHolderClick(drawable, start_location);                }            }        });        viewHolder.reduce.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                int num = product.getNumber();                if (num > 0) {                    num--;                    product.setNumber(num);                    viewHolder.shoppingNum.setText(product.getNumber()+"");                    if (callBackListener != null) {                        callBackListener.updateProduct(product, "2");                    } else {                    }                }            }        });

注意:
这里TestSectionedAdapter 继承 SectionedBaseAdapter ,大家可能会有疑问,TestSectionedAdapter 是什么东西,Google提供的API里没有这个类。没错,这就是继承BaseAdapter后的一个子类,该类中封装了右边ListView的两个item【1.列表item,2.列表中头部显示的分类名称item】可能有的人会问“列表中头部显示的分类名称是什么?”我给大家准备了一张图:

  • 下面贴出TestSectionedAdapter 代码的实现!用法跟普通的Adapter没什么区别,也是很简单的填充数据

  • 补上大家疑惑的SectionedBaseAdapter 代码!
import java.util.List;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import android.widget.Toast;public class TestSectionedAdapter extends SectionedBaseAdapter {    private Context mContext;    List pruductCagests;    private LayoutInflater mInflater;    public TestSectionedAdapter(Context context, List pruductCagests){        this.mContext = context;        this.pruductCagests = pruductCagests;        mInflater = LayoutInflater.from(context);    }    @Override    public Object getItem(int section, int position) {        return pruductCagests.get(section).getPruducts().get(position);    }    @Override    public long getItemId(int section, int position) {        return position;    }    @Override    public int getSectionCount() {        return pruductCagests.size();    }    @Override    public int getCountForSection(int section) {        return pruductCagests.get(section).getPruducts().size();    }    @Override    public View getItemView(final int section, final int position, View convertView, ViewGroup parent) {        final ViewHolder viewHolder;        if (convertView == null) {            convertView = mInflater.inflate(R.layout.list_item, null);            viewHolder = new ViewHolder();            viewHolder.image = (ImageView) convertView.findViewById(R.id.image);            viewHolder.name = (TextView) convertView.findViewById(R.id.textItem);            convertView.setTag(viewHolder);        } else {            viewHolder = (ViewHolder) convertView.getTag();        }        viewHolder.name.setText(pruductCagests.get(section).getPruducts().get(position).getName());        convertView.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                Toast.makeText(mContext, pruductCagests.get(section).getPruducts().get(position).getName(), Toast.LENGTH_SHORT).show();            }        });        return convertView;    }    class ViewHolder {        public  TextView name;        public ImageView image;    }//这里主要是右边ListView头部分类信息展示的实现    @Override    public View getSectionHeaderView(int section, View convertView, ViewGroup parent) {        LinearLayout layout = null;        if (convertView == null) {            LayoutInflater inflator = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);            layout = (LinearLayout) inflator.inflate(R.layout.header_item, null);        } else {            layout = (LinearLayout) convertView;        }        layout.setClickable(false);        ((TextView) layout.findViewById(R.id.textItem)).setText(pruductCagests.get(section).getCagName());        return layout;    }}

以下是SectionedBaseAdapter 代码的实现
代码有点长,不过只是为了实现效果的朋友完全可以不用深入了解,你只需要知道TestSectionedAdapter 是如何实现的即可,不过喜欢钻研的朋友还是可以好好看看这里面到底是如何写的。

import za.co.immedia.pinnedheaderlistviewexample.PinnedHeaderListView.PinnedSectionedHeaderAdapter;import android.util.SparseArray;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;public abstract class SectionedBaseAdapter extends BaseAdapter implements PinnedSectionedHeaderAdapter {    private static int HEADER_VIEW_TYPE = 0;    private static int ITEM_VIEW_TYPE = 0;    /**     * Holds the calculated values of @{link getPositionInSectionForPosition}     */    private SparseArray mSectionPositionCache;    /**     * Holds the calculated values of @{link getSectionForPosition}     */    private SparseArray mSectionCache;    /**     * Holds the calculated values of @{link getCountForSection}     */    private SparseArray mSectionCountCache;    /**     * Caches the item count     */    private int mCount;    /**     * Caches the section count     */    private int mSectionCount;    public SectionedBaseAdapter() {        super();        mSectionCache = new SparseArray();        mSectionPositionCache = new SparseArray();        mSectionCountCache = new SparseArray();        mCount = -1;        mSectionCount = -1;    }    @Override    public void notifyDataSetChanged() {        mSectionCache.clear();        mSectionPositionCache.clear();        mSectionCountCache.clear();        mCount = -1;        mSectionCount = -1;        super.notifyDataSetChanged();    }    @Override    public void notifyDataSetInvalidated() {        mSectionCache.clear();        mSectionPositionCache.clear();        mSectionCountCache.clear();        mCount = -1;        mSectionCount = -1;        super.notifyDataSetInvalidated();    }    @Override    public final int getCount() {        if (mCount >= 0) {            return mCount;        }        int count = 0;        for (int i = 0; i < internalGetSectionCount(); i++) {            count += internalGetCountForSection(i);            count++; // for the header view        }        mCount = count;        return count;    }    @Override    public final Object getItem(int position) {        return getItem(getSectionForPosition(position), getPositionInSectionForPosition(position));    }    @Override    public final long getItemId(int position) {        return getItemId(getSectionForPosition(position), getPositionInSectionForPosition(position));    }    @Override    public final View getView(int position, View convertView, ViewGroup parent) {        if (isSectionHeader(position)) {            return getSectionHeaderView(getSectionForPosition(position), convertView, parent);        }        return getItemView(getSectionForPosition(position), getPositionInSectionForPosition(position), convertView, parent);    }    @Override    public final int getItemViewType(int position) {        if (isSectionHeader(position)) {            return getItemViewTypeCount() + getSectionHeaderViewType(getSectionForPosition(position));        }        return getItemViewType(getSectionForPosition(position), getPositionInSectionForPosition(position));    }    @Override    public final int getViewTypeCount() {        return getItemViewTypeCount() + getSectionHeaderViewTypeCount();    }    public final int getSectionForPosition(int position) {        // first try to retrieve values from cache        Integer cachedSection = mSectionCache.get(position);        if (cachedSection != null) {            return cachedSection;        }        int sectionStart = 0;        for (int i = 0; i < internalGetSectionCount(); i++) {            int sectionCount = internalGetCountForSection(i);            int sectionEnd = sectionStart + sectionCount + 1;            if (position >= sectionStart && position < sectionEnd) {                mSectionCache.put(position, i);                return i;            }            sectionStart = sectionEnd;        }        return 0;    }    public int getPositionInSectionForPosition(int position) {        // first try to retrieve values from cache        Integer cachedPosition = mSectionPositionCache.get(position);        if (cachedPosition != null) {            return cachedPosition;        }        int sectionStart = 0;        for (int i = 0; i < internalGetSectionCount(); i++) {            int sectionCount = internalGetCountForSection(i);            int sectionEnd = sectionStart + sectionCount + 1;            if (position >= sectionStart && position < sectionEnd) {                int positionInSection = position - sectionStart - 1;                mSectionPositionCache.put(position, positionInSection);                return positionInSection;            }            sectionStart = sectionEnd;        }        return 0;    }    public final boolean isSectionHeader(int position) {        int sectionStart = 0;        for (int i = 0; i < internalGetSectionCount(); i++) {            if (position == sectionStart) {                return true;            } else if (position < sectionStart) {                return false;            }            sectionStart += internalGetCountForSection(i) + 1;        }        return false;    }    public int getItemViewType(int section, int position) {        return ITEM_VIEW_TYPE;    }    public int getItemViewTypeCount() {        return 1;    }    public int getSectionHeaderViewType(int section) {        return HEADER_VIEW_TYPE;    }    public int getSectionHeaderViewTypeCount() {        return 1;    }    public abstract Object getItem(int section, int position);    public abstract long getItemId(int section, int position);    public abstract int getSectionCount();    public abstract int getCountForSection(int section);    public abstract View getItemView(int section, int position, View convertView, ViewGroup parent);    public abstract View getSectionHeaderView(int section, View convertView, ViewGroup parent);    private int internalGetCountForSection(int section) {        Integer cachedSectionCount = mSectionCountCache.get(section);        if (cachedSectionCount != null) {            return cachedSectionCount;        }        int sectionCount = getCountForSection(section);        mSectionCountCache.put(section, sectionCount);        return sectionCount;    }    private int internalGetSectionCount() {        if (mSectionCount >= 0) {            return mSectionCount;        }        mSectionCount = getSectionCount();        return mSectionCount;    }}

好了以上基本上就是本篇文章给Android爱好者们分享的内容,刚刚开始写博客,可能其中有很多地方写的不太到位或者文章方式有待提高,还希望各位朋友能给我提出一些意见,在日后的分享博客中能够提高自身的能力。欢迎互相交流!
爱学习,爱编程,爱生活!

后期特意整理出来一份源码,可供大家参考学习:
**

下载地址:

http://download.csdn.net/detail/jaynm/9601304

**

更多相关文章

  1. Android(安卓)View中的控件和监听方法...
  2. Hbuilder集成个推时Android和ISO中推送的区别
  3. Android(安卓)App列表之圆角ListView
  4. Android聊天列表Demo(QQ,微信,等通讯工具的聊天列表)
  5. 融云 Android(安卓)sdk 2.1+ 稳定版 UI 和 模块功能自定义(二)
  6. android ICS 4.0.3 tablet模式 插入usb线 不提示sd卡已连接 解决
  7. Android的四种监听事件处理方式
  8. Android的简单应用(三)——为你的程序添加监听器
  9. [置顶] android 从资源中获取数组

随机推荐

  1. Android(安卓)Studio OkHttpClient使用教
  2. Android(安卓)监听网络状态
  3. Android(安卓)开发中使用Intent传递数据
  4. J6 Android(安卓)eMMC 分区介绍
  5. Android屏蔽HOME键亲测可用
  6. android adb devices no permission
  7. Android架构组件(2)LifecycleRegistry 源码
  8. Android(安卓)实现 Launcher
  9. Android实现手机摄像头的自动对焦
  10. Android(安卓)系统启动流程