布局======================================================================
mian_activity==============================================================
<?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:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.xp.shoppingcart.MainActivity"> <include layout="@layout/include_toolbar" /> <ExpandableListView android:id="@+id/expandableListView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:scrollbars="none" android:divider="@null"/> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/divide_line"/> <LinearLayout android:layout_width="match_parent" android:layout_height="49dp" android:background="@android:color/white" android:gravity="center_vertical" android:orientation="horizontal"> <com.xp.shoppingcart.SmoothCheckBox android:id="@+id/cb_select_all" android:layout_width="24dp" android:layout_height="24dp" android:layout_marginLeft="15dp" /> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_weight="0.69" android:text="全选" android:textColor="#333333" android:textSize="15sp" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="end" android:orientation="vertical"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="合计" android:textColor="#333333" android:textSize="15sp" /> <TextView android:id="@+id/tv_all_money" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="¥0" android:textColor="#FE3824" android:textSize="15sp" /> </LinearLayout> <TextView android:id="@+id/tv_transport" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="运费:¥0" android:textColor="#999999" android:textSize="11sp" /> </LinearLayout> <Button android:id="@+id/btn_settlement" android:layout_width="95dp" android:layout_height="match_parent" android:layout_marginLeft="15dp" android:background="#FE3824" android:text="结算(0)" android:textColor="@android:color/white" android:textSize="16sp" /> </LinearLayout> </LinearLayout>
include_toolbar================================================================
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="48dp" android:background="@color/colorPrimary" android:gravity="center" android:minHeight="48dp"> <TextView android:id="@+id/tv_toolbar_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="购物车" android:textColor="@android:color/white" android:textSize="19sp" /> </android.support.v7.widget.Toolbar>
item_shopingcart_child=============================================================
<?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:background="@android:color/white" android:orientation="horizontal" android:paddingBottom="15dp" android:paddingRight="15dp"> <LinearLayout android:id="@+id/ll_check" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:paddingLeft="15dp" android:paddingRight="17dp"> <com.xp.shoppingcart.SmoothCheckBox android:id="@+id/cb_item" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="center_vertical" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/divide_line" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="15dp" android:orientation="horizontal"> <ImageView android:id="@+id/img_icon" android:layout_width="78dp" android:layout_height="78dp" android:src="@mipmap/ic_phone" /> <RelativeLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_marginLeft="15dp" android:layout_marginTop="8dp" android:layout_weight="1"> <TextView android:id="@+id/tv_good_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" /> <TextView android:id="@+id/tv_reduce" android:layout_width="30dp" android:layout_height="30dp" android:layout_below="@id/tv_good_name" android:layout_marginTop="6dp" android:background="@drawable/selector_shopping_cart_subtract" android:gravity="center" android:text="-" android:textColor="@color/text_666666" android:textSize="15sp" /> <EditText android:id="@+id/et_count" android:layout_width="49dp" android:layout_height="30dp" android:layout_alignTop="@+id/tv_reduce" android:layout_marginBottom="1dp" android:layout_toRightOf="@+id/tv_reduce" android:background="@drawable/bg_input_box" android:gravity="center" android:inputType="number" android:maxLength="6" android:text="1" android:textColor="@color/text_666666" android:textCursorDrawable="@null" android:textSize="12sp" /> <TextView android:id="@+id/tv_add" android:layout_width="30dp" android:layout_height="30dp" android:layout_alignTop="@+id/tv_reduce" android:layout_toRightOf="@id/et_count" android:background="@drawable/selector_shopping_cart_add" android:gravity="center" android:text="+" android:textColor="@color/text_666666" android:textSize="15sp" /> <TextView android:id="@+id/tv_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:text="¥899" android:textColor="#FE3824" android:textSize="13sp" /> </RelativeLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="18dp" android:gravity="end" android:orientation="vertical"> </LinearLayout> <ImageView android:id="@+id/img_delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginTop="5dp" android:src="@mipmap/icon_delete" /> </LinearLayout> </LinearLayout> </LinearLayout>
item_shopingcart_group============================================================
<?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:background="@android:color/white" android:orientation="vertical"> <View android:layout_width="match_parent" android:layout_height="3dp" android:background="@color/divide_line" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:padding="15dp"> <com.xp.shoppingcart.SmoothCheckBox android:id="@+id/cb_group_item" android:layout_width="24dp" android:layout_height="24dp" /> <TextView android:id="@+id/tv_position" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:layout_weight="1" android:drawableLeft="@mipmap/ic_position" android:drawablePadding="3dp" android:text="京东旗舰店发货" android:textColor="#333333" android:textSize="15sp" /> </LinearLayout> </LinearLayout>
values.attrs==================================================================
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="SmoothCheckBox"> <attr name="duration" format="integer"/> <attr name="stroke_width" format="dimension"/> <attr name="color_tick" format="color"/> <attr name="color_checked" format="color"/> <attr name="color_unchecked" format="color"/> <attr name="color_unchecked_stroke" format="color"/> </declare-styleable> </resources>
bg_4stroke_grey=================================================================
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:radius="2dp" /> <solid android:color="#00FFFFFF" /> <stroke android:width="1dp" android:color="#CCCCCC" /> </shape>
seletor_shopping_cart_add=======================================================
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/btn_shopping_cart_add_press" android:state_checked="true" /> <item android:drawable="@drawable/btn_shopping_cart_add_press" android:state_pressed="true" /> <item android:drawable="@drawable/btn_shopping_cart_add_press" android:state_selected="true" /> <item android:drawable="@drawable/btn_shopping_cart_add_normal" /> </selector>
selector_shopping_cart_subtract=====================================================
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/btn_shopping_cart_add_press" android:state_checked="true" /> <item android:drawable="@drawable/btn_shopping_cart_add_press" android:state_pressed="true" /> <item android:drawable="@drawable/btn_shopping_cart_add_press" android:state_selected="true" /> <item android:drawable="@drawable/btn_shopping_cart_add_normal" /> </selector>
MianActivity================================================================
package com.xp.shoppingcart; import android.app.Activity; import android.content.res.AssetManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.AbsListView; import android.widget.Button; import android.widget.CheckBox; import android.widget.ExpandableListView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.google.gson.Gson; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; public class MainActivity extends AppCompatActivity implements UpdateView, View.OnClickListener { private ExpandableListView mExpandableListView; private SmoothCheckBox mCbSelectAll; private TextView mTvAllMoney; private Button mBtnBuy; StringBuffer stringBuffer; GoodBean goodBean; ExpandableListAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); initEvent(); } private void initEvent() { mCbSelectAll.setOnClickListener(this); } private void initData() { //读取数据解析 AssetManager assetManager = getAssets(); try { InputStream is = assetManager.open("data.json"); BufferedReader br = new BufferedReader(new InputStreamReader(is)); stringBuffer = new StringBuffer(); String str; while ((str = br.readLine()) != null) { stringBuffer.append(str); } } catch (IOException e) { e.printStackTrace(); } Gson gson = new Gson(); goodBean = gson.fromJson(stringBuffer.toString(), GoodBean.class); mAdapter = new ExpandableListAdapter(this, goodBean); mAdapter.setChangedListener(this); mExpandableListView.setAdapter(mAdapter); //展开所有的分组 for (int i = 0; i < goodBean.getContent().size(); i++) { mExpandableListView.expandGroup(i); } } private void initView() { mExpandableListView = (ExpandableListView) findViewById(R.id.expandableListView); mCbSelectAll = (SmoothCheckBox) findViewById(R.id.cb_select_all); mTvAllMoney = (TextView) findViewById(R.id.tv_all_money); mBtnBuy = (Button) findViewById(R.id.btn_settlement); //去掉ExpandableListView 默认的箭头 mExpandableListView.setGroupIndicator(null); //用于列表滑动时,EditText清除焦点,收起软键盘 mExpandableListView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView absListView, int scrollState) { if (SCROLL_STATE_TOUCH_SCROLL == scrollState) { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Activity .INPUT_METHOD_SERVICE); View focusView = getCurrentFocus(); if (focusView != null) { inputMethodManager.hideSoftInputFromWindow(focusView.getWindowToken(), InputMethodManager .HIDE_NOT_ALWAYS); focusView.clearFocus(); } } } @Override public void onScroll(AbsListView absListView, int i, int i1, int i2) { } }); } @Override public void update(boolean isAllSelected, int count, int price) { mBtnBuy.setText("结算(" + count + ")"); mTvAllMoney.setText("¥" + price); mCbSelectAll.setChecked(isAllSelected); } @Override public void onClick(View view) { selectAll(); } private void selectAll() { int allCount = goodBean.getAllCount(); int allMoney = goodBean.getAllMoney(); if (!mCbSelectAll.isChecked()) { goodBean.setAllSelect(true); for (int i = 0; i < goodBean.getContent().size(); i++) { goodBean.getContent().get(i).setIsSelected(true); for (int n = 0; n < goodBean.getContent().get(i).getGoodDetail().size(); n++) { if (!goodBean.getContent().get(i).getGoodDetail().get(n).isSelected()) { allCount++; allMoney += Integer.valueOf(goodBean.getContent().get(i).getGoodDetail().get(n).getCount()) * Integer.valueOf(goodBean.getContent().get(i).getGoodDetail().get(n).getPrice()); goodBean.getContent().get(i).getGoodDetail().get(n).setIsSelected(true); } } } } else { goodBean.setAllSelect(false); for (int i = 0; i < goodBean.getContent().size(); i++) { goodBean.getContent().get(i).setIsSelected(false); for (int n = 0; n < goodBean.getContent().get(i).getGoodDetail().size(); n++) { goodBean.getContent().get(i).getGoodDetail().get(n).setIsSelected(false); } allCount = 0; allMoney = 0; } } goodBean.setAllMoney(allMoney); goodBean.setAllCount(allCount); update(goodBean.isAllSelect(), allCount, allMoney); mAdapter.notifyDataSetChanged(); } }
SmoothCheckBox==============================================================
package com.xp.shoppingcart; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.view.View; import android.view.animation.LinearInterpolator; import android.widget.Checkable; /** * Description : A custom CheckBox with animation for Android */ public class SmoothCheckBox extends View implements Checkable { private static final String KEY_INSTANCE_STATE = "InstanceState"; private static final int COLOR_TICK = Color.WHITE; private static final int COLOR_UNCHECKED = Color.WHITE; private static final int COLOR_CHECKED = Color.parseColor("#FB4846"); private static final int COLOR_FLOOR_UNCHECKED = Color.parseColor("#DFDFDF"); private static final int DEF_DRAW_SIZE = 25; private static final int DEF_ANIM_DURATION = 300; private Paint mPaint, mTickPaint, mFloorPaint; private Point[] mTickPoints; private Point mCenterPoint; private Path mTickPath; private float mLeftLineDistance, mRightLineDistance, mDrewDistance; private float mScaleVal = 1.0f, mFloorScale = 1.0f; private int mWidth, mAnimDuration, mStrokeWidth; private int mCheckedColor, mUnCheckedColor, mFloorColor, mFloorUnCheckedColor; private boolean mChecked; private boolean mTickDrawing; private OnCheckedChangeListener mListener; public SmoothCheckBox(Context context) { this(context, null); } public SmoothCheckBox(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SmoothCheckBox(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); } private void init(AttributeSet attrs) { TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SmoothCheckBox); int tickColor = ta.getColor(R.styleable.SmoothCheckBox_color_tick, COLOR_TICK); mAnimDuration = ta.getInt(R.styleable.SmoothCheckBox_duration, DEF_ANIM_DURATION); mFloorColor = ta.getColor(R.styleable.SmoothCheckBox_color_unchecked_stroke, COLOR_FLOOR_UNCHECKED); mCheckedColor = ta.getColor(R.styleable.SmoothCheckBox_color_checked, COLOR_CHECKED); mUnCheckedColor = ta.getColor(R.styleable.SmoothCheckBox_color_unchecked, COLOR_UNCHECKED); mStrokeWidth = ta.getDimensionPixelSize(R.styleable.SmoothCheckBox_stroke_width, TextSizeUtils.dip2px(getContext(), 0)); ta.recycle(); mFloorUnCheckedColor = mFloorColor; mTickPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTickPaint.setStyle(Paint.Style.STROKE); mTickPaint.setStrokeCap(Paint.Cap.ROUND); mTickPaint.setColor(tickColor); mFloorPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mFloorPaint.setStyle(Paint.Style.FILL); mFloorPaint.setColor(mFloorColor); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(mCheckedColor); mTickPath = new Path(); mCenterPoint = new Point(); mTickPoints = new Point[3]; mTickPoints[0] = new Point(); mTickPoints[1] = new Point(); mTickPoints[2] = new Point(); setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { toggle(); mTickDrawing = false; mDrewDistance = 0; if (isChecked()) { startCheckedAnimation(); } else { startUnCheckedAnimation(); } } }); } @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(KEY_INSTANCE_STATE, super.onSaveInstanceState()); bundle.putBoolean(KEY_INSTANCE_STATE, isChecked()); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; boolean isChecked = bundle.getBoolean(KEY_INSTANCE_STATE); setChecked(isChecked); super.onRestoreInstanceState(bundle.getParcelable(KEY_INSTANCE_STATE)); return; } super.onRestoreInstanceState(state); } @Override public boolean isChecked() { return mChecked; } @Override public void toggle() { this.setChecked(!isChecked()); } @Override public void setChecked(boolean checked) { mChecked = checked; reset(); invalidate(); if (mListener != null) { mListener.onCheckedChanged(SmoothCheckBox.this, mChecked); } } /** * checked with animation * * @param checked checked * @param animate change with animation */ public void setChecked(boolean checked, boolean animate) { if (animate) { mTickDrawing = false; mChecked = checked; mDrewDistance = 0f; if (checked) { startCheckedAnimation(); } else { startUnCheckedAnimation(); } if (mListener != null) { mListener.onCheckedChanged(SmoothCheckBox.this, mChecked); } } else { this.setChecked(checked); } } private void reset() { mTickDrawing = true; mFloorScale = 1.0f; mScaleVal = isChecked() ? 0f : 1.0f; mFloorColor = isChecked() ? mCheckedColor : mFloorUnCheckedColor; mDrewDistance = isChecked() ? (mLeftLineDistance + mRightLineDistance) : 0; } private int measureSize(int measureSpec) { int defSize = TextSizeUtils.dip2px(getContext(), DEF_DRAW_SIZE); int specSize = MeasureSpec.getSize(measureSpec); int specMode = MeasureSpec.getMode(measureSpec); int result = 0; switch (specMode) { case MeasureSpec.UNSPECIFIED: case MeasureSpec.AT_MOST: result = Math.min(defSize, specSize); break; case MeasureSpec.EXACTLY: result = specSize; break; } return result; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(measureSize(widthMeasureSpec), measureSize(heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { mWidth = getMeasuredWidth(); mStrokeWidth = (mStrokeWidth == 0 ? getMeasuredWidth() / 10 : mStrokeWidth); mStrokeWidth = mStrokeWidth > getMeasuredWidth() / 5 ? getMeasuredWidth() / 5 : mStrokeWidth; mStrokeWidth = (mStrokeWidth < 3) ? 3 : mStrokeWidth; mCenterPoint.x = mWidth / 2; mCenterPoint.y = getMeasuredHeight() / 2; mTickPoints[0].x = Math.round((float) getMeasuredWidth() / 30 * 7); mTickPoints[0].y = Math.round((float) getMeasuredHeight() / 30 * 14); mTickPoints[1].x = Math.round((float) getMeasuredWidth() / 30 * 13); mTickPoints[1].y = Math.round((float) getMeasuredHeight() / 30 * 20); mTickPoints[2].x = Math.round((float) getMeasuredWidth() / 30 * 22); mTickPoints[2].y = Math.round((float) getMeasuredHeight() / 30 * 10); mLeftLineDistance = (float) Math.sqrt(Math.pow(mTickPoints[1].x - mTickPoints[0].x, 2) + Math.pow(mTickPoints[1].y - mTickPoints[0].y, 2)); mRightLineDistance = (float) Math.sqrt(Math.pow(mTickPoints[2].x - mTickPoints[1].x, 2) + Math.pow(mTickPoints[2].y - mTickPoints[1].y, 2)); mTickPaint.setStrokeWidth(mStrokeWidth); } @Override protected void onDraw(Canvas canvas) { drawBorder(canvas); drawCenter(canvas); drawTick(canvas); } private void drawCenter(Canvas canvas) { mPaint.setColor(mUnCheckedColor); float radius = (mCenterPoint.x - mStrokeWidth) * mScaleVal; canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, radius, mPaint); } private void drawBorder(Canvas canvas) { mFloorPaint.setColor(mFloorColor); int radius = mCenterPoint.x; canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, radius * mFloorScale, mFloorPaint); } private void drawTick(Canvas canvas) { if (mTickDrawing && isChecked()) { drawTickPath(canvas); } } private void drawTickPath(Canvas canvas) { mTickPath.reset(); // draw left of the tick if (mDrewDistance < mLeftLineDistance) { float step = (mWidth / 20.0f) < 3 ? 3 : (mWidth / 20.0f); mDrewDistance += step; float stopX = mTickPoints[0].x + (mTickPoints[1].x - mTickPoints[0].x) * mDrewDistance / mLeftLineDistance; float stopY = mTickPoints[0].y + (mTickPoints[1].y - mTickPoints[0].y) * mDrewDistance / mLeftLineDistance; mTickPath.moveTo(mTickPoints[0].x, mTickPoints[0].y); mTickPath.lineTo(stopX, stopY); canvas.drawPath(mTickPath, mTickPaint); if (mDrewDistance > mLeftLineDistance) { mDrewDistance = mLeftLineDistance; } } else { mTickPath.moveTo(mTickPoints[0].x, mTickPoints[0].y); mTickPath.lineTo(mTickPoints[1].x, mTickPoints[1].y); canvas.drawPath(mTickPath, mTickPaint); // draw right of the tick if (mDrewDistance < mLeftLineDistance + mRightLineDistance) { float stopX = mTickPoints[1].x + (mTickPoints[2].x - mTickPoints[1].x) * (mDrewDistance - mLeftLineDistance) / mRightLineDistance; float stopY = mTickPoints[1].y - (mTickPoints[1].y - mTickPoints[2].y) * (mDrewDistance - mLeftLineDistance) / mRightLineDistance; mTickPath.reset(); mTickPath.moveTo(mTickPoints[1].x, mTickPoints[1].y); mTickPath.lineTo(stopX, stopY); canvas.drawPath(mTickPath, mTickPaint); float step = (mWidth / 20) < 3 ? 3 : (mWidth / 20); mDrewDistance += step; } else { mTickPath.reset(); mTickPath.moveTo(mTickPoints[1].x, mTickPoints[1].y); mTickPath.lineTo(mTickPoints[2].x, mTickPoints[2].y); canvas.drawPath(mTickPath, mTickPaint); } } // invalidate if (mDrewDistance < mLeftLineDistance + mRightLineDistance) { postDelayed(new Runnable() { @Override public void run() { postInvalidate(); } }, 10); } } private void startCheckedAnimation() { ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 0f); animator.setDuration(mAnimDuration / 3 * 2); animator.setInterpolator(new LinearInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mScaleVal = (float) animation.getAnimatedValue(); mFloorColor = getGradientColor(mUnCheckedColor, mCheckedColor, 1 - mScaleVal); postInvalidate(); } }); animator.start(); ValueAnimator floorAnimator = ValueAnimator.ofFloat(1.0f, 0.8f, 1.0f); floorAnimator.setDuration(mAnimDuration); floorAnimator.setInterpolator(new LinearInterpolator()); floorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mFloorScale = (float) animation.getAnimatedValue(); postInvalidate(); } }); floorAnimator.start(); drawTickDelayed(); } private void startUnCheckedAnimation() { ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f); animator.setDuration(mAnimDuration); animator.setInterpolator(new LinearInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mScaleVal = (float) animation.getAnimatedValue(); mFloorColor = getGradientColor(mCheckedColor, mFloorUnCheckedColor, mScaleVal); postInvalidate(); } }); animator.start(); ValueAnimator floorAnimator = ValueAnimator.ofFloat(1.0f, 0.8f, 1.0f); floorAnimator.setDuration(mAnimDuration); floorAnimator.setInterpolator(new LinearInterpolator()); floorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mFloorScale = (float) animation.getAnimatedValue(); postInvalidate(); } }); floorAnimator.start(); } private void drawTickDelayed() { postDelayed(new Runnable() { @Override public void run() { mTickDrawing = true; postInvalidate(); } }, mAnimDuration); } private static int getGradientColor(int startColor, int endColor, float percent) { int startA = Color.alpha(startColor); int startR = Color.red(startColor); int startG = Color.green(startColor); int startB = Color.blue(startColor); int endA = Color.alpha(endColor); int endR = Color.red(endColor); int endG = Color.green(endColor); int endB = Color.blue(endColor); int currentA = (int) (startA * (1 - percent) + endA * percent); int currentR = (int) (startR * (1 - percent) + endR * percent); int currentG = (int) (startG * (1 - percent) + endG * percent); int currentB = (int) (startB * (1 - percent) + endB * percent); return Color.argb(currentA, currentR, currentG, currentB); } public void setOnCheckedChangeListener(OnCheckedChangeListener l) { this.mListener = l; } public interface OnCheckedChangeListener { void onCheckedChanged(SmoothCheckBox checkBox, boolean isChecked); } }
TextSizeUtiles==================================================================
package com.xp.shoppingcart; import android.content.Context; public class TextSizeUtils { /** * 将px值转换为dip或dp值,保证尺寸大小不变 * * @param pxValue (DisplayMetrics类中属性density) * @return */ public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } /** * 将dip或dp值转换为px值,保证尺寸大小不变 * * @param dipValue (DisplayMetrics类中属性density) * @return */ public static int dip2px(Context context, float dipValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dipValue * scale + 0.5f); } /** * 将px值转换为sp值,保证文字大小不变 * * @param pxValue (DisplayMetrics类中属性scaledDensity) * @return */ public static int px2sp(Context context, float pxValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (pxValue / fontScale + 0.5f); } /** * 将sp值转换为px值,保证文字大小不变 * * @param spValue (DisplayMetrics类中属性scaledDensity) * @return */ public static int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } }
UpdateView================================================================
package com.xp.shoppingcart; /** * 更新数据的回调接口 */ public interface UpdateView { void update(boolean isAllSelected, int count, int price); }
ExpandableListAdapter=============================================================
package com.xp.shoppingcart; import android.content.Context; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; public class ExpandableListAdapter extends BaseExpandableListAdapter { private static final String TAG = "ExpandableListAdapter"; private Context mContext; private GoodBean goodBean; private UpdateView updateViewListener; protected static final int KEY_DATA = 0xFFF11133; public ExpandableListAdapter(Context context, GoodBean goodBean) { this.mContext = context; this.goodBean = goodBean; } @Override public int getGroupCount() { return goodBean.getContent().size(); } @Override public int getChildrenCount(int groupPosition) { return goodBean.getContent().get(groupPosition).getGoodDetail().size(); } @Override public Object getGroup(int groupPosition) { return goodBean.getContent().get(groupPosition); } @Override public Object getChild(int groupPosition, int childPosition) { return goodBean.getContent().get(groupPosition).getGoodDetail().get(childPosition); } @Override public long getGroupId(int groupPosition) { return groupPosition; } @Override public long getChildId(int groupPosition, int childPosition) { return childPosition; } @Override public boolean hasStableIds() { return false; } @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { GroupViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.item_shopingcart_group, parent, false); holder = new GroupViewHolder(convertView); convertView.setTag(holder); } else { holder = (GroupViewHolder) convertView.getTag(); } holder.cbGroupItem.setTag(groupPosition); holder.cbGroupItem.setOnClickListener(listener); holder.tvPosition.setText(goodBean.getContent().get(groupPosition).getAddress()); //根据获取的状态设置是否被选中 if (goodBean.getContent().get(groupPosition).isSelected()) { if (!holder.cbGroupItem.isChecked()) { holder.cbGroupItem.setChecked(true); } } else { holder.cbGroupItem.setChecked(false); } return convertView; } @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { ChildViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.item_shopingcart_child, parent, false); holder = new ChildViewHolder(convertView); convertView.setTag(holder); } else { holder = (ChildViewHolder) convertView.getTag(); } String tag = groupPosition + "," + childPosition; holder.cbItem.setTag(tag); holder.tvReduce.setTag(tag); holder.tvAdd.setTag(tag); holder.imgDelete.setTag(tag); holder.imgIcon.setTag(tag); holder.cbItem.setOnClickListener(listener); holder.tvReduce.setOnClickListener(listener); //添加商品数量 holder.tvAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String tag = view.getTag().toString(); String[] split; int groupId = 0; int childId = 0; int allCount = goodBean.getAllCount(); int allMoney; if (tag.contains(",")) { split = tag.split(","); groupId = Integer.parseInt(split[0]); childId = Integer.parseInt(split[1]); } String goodCount = goodBean.getContent().get(groupId).getGoodDetail().get(childId).getCount(); goodBean.getContent().get(groupId).getGoodDetail().get(childId).setCount(addCount(goodCount)); allMoney = goodBean.getAllMoney(); if (goodBean.getContent().get(groupId).getGoodDetail().get(childId).isSelected()) { allMoney += Integer.valueOf(goodBean.getContent().get(groupId).getGoodDetail().get(childId).getPrice()); updateViewListener.update(goodBean.isAllSelect(), allCount, allMoney); } goodBean.setAllMoney(allMoney); notifyDataSetChanged(); } }); holder.imgDelete.setOnClickListener(listener); //根据获取的状态设置是否被选中 if (goodBean.getContent().get(groupPosition).getGoodDetail().get(childPosition).isSelected()) { holder.cbItem.setChecked(true); } else { holder.cbItem.setChecked(false); } //设置数据 holder.tvPrice.setText("¥" + goodBean.getContent().get(groupPosition).getGoodDetail().get(childPosition).getPrice()); holder.tvGoodName.setText(goodBean.getContent().get(groupPosition).getGoodDetail().get(childPosition).getName()); //对商品数量的监听 EditTextWatcher textWatcher = (EditTextWatcher) holder.etCount.getTag(KEY_DATA); if (textWatcher != null) { holder.etCount.removeTextChangedListener(textWatcher); } holder.etCount.setText(String.valueOf(goodBean.getContent().get(groupPosition).getGoodDetail().get(childPosition).getCount())); EditTextWatcher watcher = new EditTextWatcher(goodBean.getContent().get(groupPosition).getGoodDetail().get(childPosition)); holder.etCount.setTag(KEY_DATA, watcher); holder.etCount.addTextChangedListener(watcher); holder.etCount.setText(goodBean.getContent().get(groupPosition).getGoodDetail().get(childPosition).getCount()); return convertView; } /** * 商品数量EditText内容改变的监听 */ class EditTextWatcher implements TextWatcher { private GoodBean.ContentBean.GoodDetailBean GoodDetail; public EditTextWatcher(GoodBean.ContentBean.GoodDetailBean item) { this.GoodDetail = item; } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if (!TextUtils.isEmpty(s.toString().trim())) { String textNum = s.toString().trim(); GoodDetail.setCount(textNum); } } } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return false; } View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { SmoothCheckBox checkBox; String tag = v.getTag().toString(); String[] split; int groupId = 0; int childId = 0; int childSize = 0; int groupPosition = 0; int allCount = goodBean.getAllCount();//被选中的item数量 int allMoney = goodBean.getAllMoney(); if (tag.contains(",")) { split = tag.split(","); groupId = Integer.parseInt(split[0]); childId = Integer.parseInt(split[1]); } else { groupPosition = Integer.parseInt(tag); childSize = goodBean.getContent().get(groupPosition).getGoodDetail().size(); } switch (v.getId()) { case R.id.cb_group_item: checkBox = (SmoothCheckBox) v; //根据父checkbox的选中状态设置存储数据里面商品是否被选中 goodBean.getContent().get(groupPosition).setIsSelected(!checkBox.isChecked()); if (!checkBox.isChecked()) { for (int i = 0; i < childSize; i++) { if (!goodBean.getContent().get(groupPosition).getGoodDetail().get(i).isSelected()) { allCount++; goodBean.getContent().get(groupPosition).getGoodDetail().get(i).setIsSelected(!checkBox.isChecked()); allMoney += Integer.valueOf(goodBean.getContent().get(groupPosition).getGoodDetail().get(i).getCount()) * Integer.valueOf(goodBean.getContent().get(groupPosition).getGoodDetail().get(i).getPrice()); } } } else { allCount -= childSize; for (int i = 0; i < childSize; i++) { goodBean.getContent().get(groupPosition).getGoodDetail().get(i).setIsSelected(!checkBox.isChecked()); allMoney -= Integer.valueOf(goodBean.getContent().get(groupPosition).getGoodDetail().get(i).getCount()) * Integer.valueOf(goodBean.getContent().get(groupPosition).getGoodDetail().get(i).getPrice()); } } //父item选中的数量 int fCount = 0; //判断是否所有的父item都被选中,决定全选按钮状态 for (int i = 0; i < goodBean.getContent().size(); i++) { if (goodBean.getContent().get(i).isSelected()) { fCount++; } } if (fCount == goodBean.getContent().size()) { goodBean.setAllSelect(true); } else { goodBean.setAllSelect(false); } goodBean.setAllCount(allCount); goodBean.setAllMoney(allMoney); notifyDataSetChanged(); updateViewListener.update(goodBean.isAllSelect(), allCount, allMoney); break; //单个子项item被点击 case R.id.cb_item: checkBox = (SmoothCheckBox) v; int cCount = 0;//子item被选中的数量 int fcCount = 0;//父item被选中的数量 goodBean.getContent().get(groupId).getGoodDetail().get(childId).setIsSelected(!checkBox.isChecked()); //遍历父item所有数据,统计被选中的item数量 for (int i = 0; i < goodBean.getContent().get(groupId).getGoodDetail().size(); i++) { if (goodBean.getContent().get(groupId).getGoodDetail().get(i).isSelected()) { cCount++; } } //判断是否所有的子item都被选中,决定父item状态 if (cCount == goodBean.getContent().get(groupId).getGoodDetail().size()) { goodBean.getContent().get(groupId).setIsSelected(true); } else { goodBean.getContent().get(groupId).setIsSelected(false); } //判断是否所有的父item都被选中,决定全选按钮状态 for (int i = 0; i < goodBean.getContent().size(); i++) { if (goodBean.getContent().get(i).isSelected()) { fcCount++; } } if (fcCount == goodBean.getContent().size()) { goodBean.setAllSelect(true); } else { goodBean.setAllSelect(false); } //判断子item状态,更新结算总商品数和合计Money if (!checkBox.isChecked()) { allCount++; allMoney += Integer.valueOf(goodBean.getContent().get(groupId).getGoodDetail().get(childId).getCount()) * Integer.valueOf(goodBean.getContent().get(groupId).getGoodDetail().get(childId).getPrice()); } else { allCount--; allMoney -= Integer.valueOf(goodBean.getContent().get(groupId).getGoodDetail().get(childId).getCount()) * Integer.valueOf(goodBean.getContent().get(groupId).getGoodDetail().get(childId).getPrice()); } goodBean.setAllCount(allCount); goodBean.setAllMoney(allMoney); notifyDataSetChanged(); updateViewListener.update(goodBean.isAllSelect(), allCount, allMoney); break; case R.id.tv_reduce: //减少商品数量 String goodCount = goodBean.getContent().get(groupId).getGoodDetail().get(childId).getCount(); if (Integer.valueOf(goodCount) > 1) { goodBean.getContent().get(groupId).getGoodDetail().get(childId).setCount(reduceCount(goodCount)); if (goodBean.getContent().get(groupId).getGoodDetail().get(childId).isSelected()) { allMoney -= Integer.valueOf(goodBean.getContent().get(groupId).getGoodDetail().get(childId).getPrice()); updateViewListener.update(goodBean.isAllSelect(), allCount, allMoney); } goodBean.setAllMoney(allMoney); notifyDataSetChanged(); } break; case R.id.img_delete: goodBean.getContent().get(groupId).getGoodDetail().remove(childId); if (goodBean.getContent().get(groupId).getGoodDetail().size() == 0) { goodBean.getContent().remove(groupId); } notifyDataSetChanged(); break; } } }; public void setChangedListener(UpdateView listener) { if (updateViewListener == null) { this.updateViewListener = listener; } } private String addCount(String var) { Integer integer = Integer.valueOf(var); integer++; return integer + ""; } private String reduceCount(String var) { Integer integer = Integer.valueOf(var); if (integer > 1) { integer--; } return integer + ""; } static class GroupViewHolder { SmoothCheckBox cbGroupItem; TextView tvPosition; GroupViewHolder(View view) { cbGroupItem = (SmoothCheckBox) view.findViewById(R.id.cb_group_item); tvPosition = (TextView) view.findViewById(R.id.tv_position); } } static class ChildViewHolder { SmoothCheckBox cbItem; TextView tvPrice,tvGoodName; EditText etCount; TextView tvReduce; TextView tvAdd; ImageView imgDelete; ImageView imgIcon; ChildViewHolder(View view) { cbItem = (SmoothCheckBox) view.findViewById(R.id.cb_item); tvPrice = (TextView) view.findViewById(R.id.tv_price); tvGoodName = (TextView) view.findViewById(R.id.tv_good_name); etCount = (EditText) view.findViewById(R.id.et_count); tvReduce = (TextView) view.findViewById(R.id.tv_reduce); tvAdd = (TextView) view.findViewById(R.id.tv_add); imgDelete = (ImageView) view.findViewById(R.id.img_delete); imgIcon = (ImageView) view.findViewById(R.id.img_icon); } } }
GoodBean====================================================================
package com.xp.shoppingcart; import java.util.List; public class GoodBean { private List<ContentBean> content; private int allMoney; private int allCount; private boolean isAllSelect; public boolean isAllSelect() { return isAllSelect; } public void setAllSelect(boolean allSelect) { isAllSelect = allSelect; } public int getAllMoney() { return allMoney; } public void setAllMoney(int allMoney) { this.allMoney = allMoney; } public int getAllCount() { return allCount; } public void setAllCount(int allCount) { this.allCount = allCount; } public List<ContentBean> getContent() { return content; } public void setContent(List<ContentBean> content) { this.content = content; } public static class ContentBean { private String id; private String address; private boolean isSelected; private List<GoodDetailBean> goodDetail; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getAddress() { return address; } public void setAdress(String address) { this.address = address; } public boolean isSelected() { return isSelected; } public void setIsSelected(boolean isSelected) { this.isSelected = isSelected; } public List<GoodDetailBean> getGoodDetail() { return goodDetail; } public void setGoodDetail(List<GoodDetailBean> goodDetail) { this.goodDetail = goodDetail; } public static class GoodDetailBean { private String id; private String pic; private String count; private String name; private String price; private boolean isEdit; private boolean isSelected; public boolean isEdit() { return isEdit; } public boolean isSelected() { return isSelected; } public void setIsSelected(boolean isSelected) { this.isSelected = isSelected; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getPic() { return pic; } public void setPic(String pic) { this.pic = pic; } public String getCount() { return count; } public void setCount(String count) { this.count = count; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; } public boolean isIsedit() { return isEdit; } public void setIsEdit(boolean isEdit) { this.isEdit = isEdit; } } } }
依赖======================================================================
apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion "26.0.2" defaultConfig { applicationId "com.xp.shoppingcart" minSdkVersion 15 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.3.1' compile 'com.android.support.constraint:constraint-layout:1.0.2' testCompile 'junit:junit:4.12' compile 'com.google.code.gson:gson:2.2.4' }