我叫小马,我在坚持写一些东西,希望互相学习.我的博客是http://blog.csdn.net/maqianli23
------------------------------------------------------------------------------------------------------------------------------------------------
介绍一下我在项目中写过的一个购物车。有以下功能:左滑删除商品;全选、多选、单选结算价钱,删除商品,增减商品数量
一般购物车运用到功能就这些了吧。接下来主要介绍一下他的左滑删除共呢个完整代码已经抽取成了一个demo.我会在最后把地址贴出来。其实原理很简单,就是ExpandableListView扩展成一个DelSlideExpandableListView,控件一共有两个,一个DelSlideExpandableListView,和一个包裹着子视图可滑动部分的ScrollLinerLayout.
接下来看代码吧。昨天晚上我 把所有的几乎都加上了注释 很容易理解了。
先放布局 cativity_main.xml:购物车的主体布局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.mql.shoppingcardemo.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="50dp" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="44dp" android:background="@color/colorPrimary" android:orientation="horizontal"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="购物车" android:textColor="#ffffff" android:textSize="17sp" /> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#ff0000" /> <com.example.mql.shoppingcardemo.DelSlideExpandableListView android:id="@+id/exListView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="8" android:childIndicator="@null" android:groupIndicator="@null" android:divider="@color/transparent" android:scrollbars="none"> </com.example.mql.shoppingcardemo.DelSlideExpandableListView> </LinearLayout> <LinearLayout android:id="@+id/ll_shoppingCar_bottom" android:layout_width="match_parent" android:layout_height="50dp" android:background="#ffffff" android:layout_alignParentBottom="true" android:orientation="horizontal"> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" android:background="#444444" android:gravity="center_vertical" android:orientation="horizontal"> <CheckBox android:id="@+id/all_chekbox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="10dp" android:layout_marginRight="4dp" android:checkMark="?android:attr/listChoiceIndicatorMultiple" android:checked="false" android:gravity="center" android:minHeight="64dp" android:paddingLeft="10dp" android:textAppearance="?android:attr/textAppearanceLarge" android:visibility="visible" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="2dp" android:paddingRight="10dp" android:text="全选" android:textColor="#ffffff" android:textSize="16sp" android:textStyle="bold" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:text="合计:" android:textColor="#ffffff" android:textSize="16sp" android:textStyle="bold" /> <TextView android:id="@+id/tv_total_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="¥0.00" android:textColor="#ffffff" android:textSize="16sp" android:textStyle="bold" /> </LinearLayout> <TextView android:id="@+id/tv_delete" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#949494" android:clickable="true" android:gravity="center" android:text="删除" android:textColor="#FAFAFA" /> <TextView android:id="@+id/tv_go_to_pay" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#d11e1a" android:clickable="true" android:gravity="center" android:text="结算(0)" android:textColor="#FAFAFA" /> </LinearLayout> </RelativeLayout>接下来是 shopcar_group_item.xml: 这是商铺的布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="44dp"> <View android:layout_width="match_parent" android:layout_height="8dp" android:background="#f8f8f8"/> <LinearLayout android:layout_width="match_parent" android:layout_height="43dp" android:background="@color/transparent" android:orientation="horizontal"> <CheckBox android:id="@+id/cb_checkStoreAll" android:layout_width="30dp" android:layout_height="20dp" android:layout_margin="12dp" android:layout_gravity="center" android:visibility="visible" /> <ImageView android:id="@+id/riv_store" android:layout_width="20dp" android:layout_height="20dp" android:layout_marginLeft="10dp" android:layout_gravity="center" android:src="@mipmap/ic_launcher" android:layout_marginBottom="11dp" android:layout_marginTop="11dp" android:layout_marginRight="8dp" android:scaleType="fitXY" /> <TextView android:id="@+id/tv_storeName" android:layout_width="match_parent" android:layout_height="match_parent" android:textColor="#333333" android:textSize="14sp" android:gravity="center|left" android:text="小马家的第一个马棚"/> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#e5e5e5"/> </LinearLayout>最后是shopcar_child_item.xml:这是商品的布局,里面包含了左划出现删除的布局
<com.example.mql.shoppingcardemo.ScrollLinerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/transparent" android:orientation="horizontal"> <LinearLayout android:layout_width="match_parent" android:layout_height="96dp" android:orientation="vertical"> <LinearLayout android:id="@+id/forward_layout" android:layout_width="match_parent" android:layout_height="95dp" android:gravity="center_vertical" android:orientation="horizontal"> <CheckBox android:id="@+id/cb_product" android:layout_width="0dp" android:layout_height="20dp" android:layout_gravity="center" android:layout_marginLeft="12dp" android:layout_marginRight="12dp" android:layout_weight="0.5" /> <ImageView android:id="@+id/iv_shopcar_product" android:layout_width="80dp" android:layout_height="80dp" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" android:scaleType="fitXY" android:src="@mipmap/ic_launcher" /> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_marginBottom="12dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="12dp" android:layout_weight="4" android:orientation="vertical"> <TextView android:id="@+id/tv_product_name" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:ellipsize="end" android:lines="2" android:text="hahahahahahhaahahahahahahahhahahahahaahahahahahahahhaahah" android:textColor="#333333" android:textSize="14sp" android:textStyle="bold" /> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_margin="5dp" android:layout_marginBottom="16dp" android:layout_weight="0.5" android:orientation="horizontal"> <TextView android:id="@+id/tv_product_price" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="left|center|bottom" android:text="1231" android:textColor="#d11e1a" android:textSize="17sp" android:textStyle="bold" /> <Button android:id="@+id/tv_reduce" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="center" android:background="@drawable/btn_gray" android:text="-" android:textColor="#888888" android:textStyle="bold" /> <TextView android:id="@+id/tv_product_number" android:layout_width="25dp" android:layout_height="25dp" android:gravity="center" android:singleLine="true" android:text="1" /> <Button android:id="@+id/tv_add" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="center" android:background="@drawable/btn_gray" android:text="+" android:textColor="#888888" android:textStyle="bold" /> </LinearLayout> </LinearLayout> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:background="@mipmap/view" /> </LinearLayout> <TextView android:id="@+id/delete_action" android:layout_width="@dimen/delete_action_len" android:layout_height="match_parent" android:layout_gravity="center" android:background="#d11e1a" android:gravity="center" android:padding="7dp" android:text="delete" android:textColor="#FFF" android:textSize="18dp" /> </com.example.mql.shoppingcardemo.ScrollLinerLayout>
public class DelSlideExpandableListView extends ExpandableListView implements GestureDetector.OnGestureListener, View.OnTouchListener { //手势监听 private GestureDetector mDetector; private String TAG = "xiaoma"; public DelSlideExpandableListView(Context context) { super(context); init(context); } public DelSlideExpandableListView(Context context, AttributeSet att) { super(context, att); init(context); } private int standard_touch_target_size = 0; private float mLastMotionX; // 有item被拉出 public boolean deleteView = false; // 当前拉出的view private ScrollLinerLayout mScrollLinerLayout = null; // 滑动着 private boolean scroll = false; // 禁止拖动 private boolean forbidScroll = false; // 禁止拖动 private boolean clicksameone = false; // 当前拉出的位置 private int position; // 消息冻结 private boolean freeze = false; private void init(Context mContext) { mDetector = new GestureDetector(mContext, this);//创建手势监听类 // mDetector.setIsLongpressEnabled(false); //出现删除的最大距离 standard_touch_target_size = (int) getResources().getDimension(R.dimen.delete_action_len); this.setOnTouchListener(this);//设置监听 } public void reset() { reset(false); } //重置 public void reset(boolean noaction) { position = -1; deleteView = false; if (mScrollLinerLayout != null) { if (!noaction) { //滑出 mScrollLinerLayout.snapToScreen(0); Log.e("xiaoma",22+""); } else { //滑回去 mScrollLinerLayout.scrollTo(0, 0); Log.e("xiaoma",11+""); } mScrollLinerLayout = null; } scroll = false; } public boolean onDown(MotionEvent e) {//如果手势向下滑,则禁止一切滑动动作,将其设置为不滑动状态 // Log.i(TAG, "onDown"); mLastMotionX = e.getX(); int p = this.pointToPosition((int) e.getX(), (int) e.getY()) - this.getFirstVisiblePosition(); if (deleteView) { if (p != position) { // 吃掉,不在有消息 freeze = true; return true; } else { clicksameone = true; } } position = p; scroll = false; return false; } public void onLongPress(MotionEvent e) { // Log.i(TAG, "onLongPress"); } public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // Log.i(TAG, "onScroll" + e1.getX() + ":" + distanceX); // 第二次 if (scroll) { int deltaX = (int) (mLastMotionX - e2.getX()); if (deleteView) {//如果被拉出,则拉出的距离为 deltaX += standard_touch_target_size;//一直在变da } if (deltaX >= 0 && deltaX <= standard_touch_target_size) {//判断是否在拉出 mScrollLinerLayout.scrollBy(deltaX - mScrollLinerLayout.getScrollX(), 0);//横向滑动 } return true; } if (!forbidScroll) {//没有禁止滑动 forbidScroll = true; // x方向滑动,才开始拉动 if (Math.abs(distanceX) > Math.abs(distanceY)) {//判断横向滑动大于Y View v = this.getChildAt(position); boolean ischild = v instanceof ScrollLinerLayout; if (ischild) { mScrollLinerLayout = (ScrollLinerLayout) v; scroll = true; int deltaX = (int) (mLastMotionX - e2.getX()); if (deleteView) { // 再次点击的时候,要把deltax增加 deltaX += standard_touch_target_size; } if (deltaX >= 0 && deltaX <= standard_touch_target_size) { mScrollLinerLayout.scrollBy((int) (e1.getX() - e2.getX()), 0); } } } } return false; } public void onShowPress(MotionEvent e) { // Log.i(TAG, "onShowPress"); } public boolean onSingleTapUp(MotionEvent e) { // Log.i(TAG, "onSingleTapUp"); return false; } @Override public boolean onTouchEvent(MotionEvent event) { if (scroll || deleteView) { return true; } return super.onTouchEvent(event); } @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { boolean isfreeze = freeze; boolean isclicksameone = clicksameone; forbidScroll = false; clicksameone = false; freeze = false; if (isfreeze) { // 上一个跟当前点击不一致 还原 reset(); return true; } int deltaX2 = (int) (mLastMotionX - event.getX()); // 不存在 // Log.i(TAG, "scroll:" + scroll + "deltaX2:" + deltaX2); if (scroll && deltaX2 >= standard_touch_target_size / 2) { mScrollLinerLayout.snapToScreen(standard_touch_target_size); deleteView = true; scroll = false; return true; } if (deleteView && scroll && deltaX2 >= -standard_touch_target_size / 2) { mScrollLinerLayout.snapToScreen(standard_touch_target_size); deleteView = true; scroll = false; return true; } if(isclicksameone||scroll){ reset(); return true; } reset(); } if (freeze) { return true; } // Log.i(TAG, "onTouchEvent"); return mDetector.onTouchEvent(event); } public void deleteItem() { Log.i(TAG, "deleteItem"); reset(true); } /* * (non-Javadoc) * * @see android.view.GestureDetector.OnGestureListener#onFling(android.view. * MotionEvent, android.view.MotionEvent, float, float) */ @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return false; } }
然后是ScrollLinerLayout
public class ScrollLinerLayout extends LinearLayout { public ScrollLinerLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context) { mScroller = new Scroller(context); } private Scroller mScroller; @Override public void computeScroll() { // Log.i("Scroller", "computeScroll"); if (mScroller.computeScrollOffset()) {//判断滑动是否结束 scrollTo(mScroller.getCurrX(), 0);//估算滑动位移,使其滑动到指定位置 postInvalidate();//刷新屏幕 ,调用后它会用handler通知UI线程重绘屏幕, } } public void snapToScreen(int whichScreen) { int curscrollerx = getScrollX();//得到目前的X坐标 mScroller.startScroll(curscrollerx, 0, whichScreen - curscrollerx, 0, 500);//开始滚动起点,滚动的距离距离,和滚动的持续时间 invalidate();//刷新View,必须是在UI线程中进行工作,重回界面 } }
先上传一个百度云地址:
链接: https://pan.baidu.com/s/1kVNl7wj 密码: ccc9
我的github已经弄好了。。。
传送门:https://github.com/yuyunhai/shoppingCar