MVP框架实现二级购物车

布局文件:activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".view.activitys.MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:background="@android:color/holo_red_light"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:text="购物车"
            android:padding="5dp"
            />
    </RelativeLayout>

    <ExpandableListView
        android:id="@+id/shopexpand"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="8"
        android:layout_marginLeft="10dp"
        android:groupIndicator="@null"
        >

    </ExpandableListView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="65dp"
        android:layout_alignParentBottom="true"
        android:gravity="center_vertical"
        >

        <com.example.zer.text01.view.activitys.SmoothCheckBox
            android:id="@+id/checkAll"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_marginLeft="15dp"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:text="全选/反选"
            android:textColor="#333333"
            android:textSize="15sp" />


        <TextView
            android:id="@+id/total_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="50dp"
            android:textSize="20sp"
            android:textColor="#e53e42"
            android:text="总价 : ¥0元"
            android:gravity="center"
            />

        <TextView
            android:id="@+id/total_count"
            android:gravity="center"
            android:textSize="24dp"
            android:text="去结算"
            android:layout_marginLeft="100dp"
            android:textColor="@android:color/black"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#f00"/>
    </LinearLayout>
</LinearLayout>

---------------------------------------------------------------------------------------------------------------------------

二级列表的父布局文件:groupview.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:paddingTop="20dp"
    android:paddingBottom="20dp">

    <com.example.zer.text01.view.activitys.SmoothCheckBox
        android:id="@+id/cb_group"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_gravity="center_vertical"
        />

    <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:scaleType="centerCrop"
        android:background="@mipmap/dianpu"
        android:layout_marginLeft="15dp"
        android:layout_gravity="center_vertical"
        />

    <TextView
        android:id="@+id/group_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_gravity="center_vertical"
        android:textSize="14sp"
        />
</LinearLayout>

----------------------------------------------------------------------------------------------------------------

二级列表的子布局文件:childview.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:paddingLeft="30dp">

    <com.example.zer.text01.view.activitys.SmoothCheckBox
        android:id="@+id/cb_child"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_gravity="center_vertical"
        />

    <ImageView
        android:id="@+id/child_img"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:scaleType="fitXY"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp"
        />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp"
        >

        <TextView
            android:id="@+id/child_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="false"
            />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            >

            <TextView
                android:id="@+id/child_price"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="59.90"
                android:textColor="@android:color/holo_red_light"
                />

            <com.example.zer.text01.view.activitys.CustomChangeNumView
                android:id="@+id/custom"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

            </com.example.zer.text01.view.activitys.CustomChangeNumView>

            <ImageView
                android:id="@+id/child_rublish"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:background="@mipmap/rublish"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="15dp"
                />
        </LinearLayout>

    </LinearLayout>
</LinearLayout>

------------------------------------------------------------------------------------------------------------------

自定义view的布局文件:changeview.xml(加减号)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    >

    <Button
        android:background="#fff"
        android:textSize="20sp"
        android:id="@+id/child_reverse"
        android:text="一"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <EditText
        android:textStyle="bold"
        android:textSize="23sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/child_reverse"
        android:id="@+id/child_count"
        />

    <Button
        android:id="@+id/child_add"
        android:background="#fff"
        android:textSize="25sp"
        android:text="+"
        android:layout_toRightOf="@id/child_count"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

-----------------------------------------------------------------------------------------------------------------

Mainactivity中:

package com.example.zer.text01.view.activitys;

import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.TextView;

import com.example.zer.text01.Base.BaseActivity;
import com.example.zer.text01.R;
import com.example.zer.text01.model.bean.ResultBean;
import com.example.zer.text01.presenter.MainPresenter;
import com.example.zer.text01.view.IMainView;
import com.example.zer.text01.view.adapters.MyExpandAdapter;
import com.example.zer.text01.view.interfaces.UpdateView;

import java.util.HashMap;
import java.util.Map;

public class MainActivity extends BaseActivity<MainPresenter> implements IMainView,UpdateView {

    private String url="https://www.zhaoapi.cn/product/getCarts";
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            ResultBean resultBean= (ResultBean) msg.obj;
            setAdapter(resultBean);
        }
    };
    private MyExpandAdapter adapter;

    public void setAdapter(ResultBean resultBean){
        adapter = new MyExpandAdapter(MainActivity.this);
        adapter.setData(resultBean);
        adapter.setUpdateViewListener(this);
        shopExapnd.setAdapter(adapter);
        for (int i=0;i<resultBean.getData().size();i++){
            shopExapnd.expandGroup(i);
        };
    }


    private ExpandableListView shopExapnd;
    private SmoothCheckBox checkAll;
    private TextView total_price;
    private TextView total_count;

    @Override
    protected void initListener() {
        checkAll.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                checkAll.setChecked(!checkAll.isChecked());
                adapter.selectAll(checkAll.isChecked());
            }
        });
    }

    @Override
    protected void initData() {
        Map<String,String> parms=new HashMap<>();
        parms.put("uid","13850");
        getBasePresenter().getDataFromServer(url,parms);
    }

    @Override
    protected MainPresenter initPresenter() {
        return new MainPresenter();
    }

    @Override
    protected void initView() {
        //二级列表
        shopExapnd = findViewById(R.id.shopexpand);
        //全选/反选按钮
        checkAll = findViewById(R.id.checkAll);
        //总价格
        total_price = findViewById(R.id.total_price);
        //总数量
        total_count = findViewById(R.id.total_count);
    }

    @Override
    protected int getActivityLayout() {
        return R.layout.activity_main;
    }

    @Override
    public void getBackData(ResultBean resultBean) {
        Message msg=Message.obtain();
        msg.obj=resultBean;
        handler.sendMessage(msg);
    }

    //更新的方法
    @Override
    public void update(boolean isAllSelected, int count, float price) {
        checkAll.setChecked(isAllSelected);
        total_count.setText("结算"+count);
        total_price.setText("¥"+price);
    }


}

-------------------------------------------------------------------------------------------------------------------

MainPresenter:

package com.example.zer.text01.presenter;

import com.example.zer.text01.model.bean.ResultBean;
import com.example.zer.text01.model.net.MainMoudle;
import com.example.zer.text01.view.IMainView;

import java.util.Map;

public class MainPresenter extends BasePresenter<IMainView> implements DataBack{

    private MainMoudle mainMoudle;

    public MainPresenter() {
        this.mainMoudle = new MainMoudle();
    }

    public void getDataFromServer(String url, Map<String,String> parms){
        mainMoudle.getShopCarData(url,parms,this);
    }

    @Override
    public void callbackSuccess(ResultBean resultBean) {
        getBaseView().getBackData(resultBean);
    }

    @Override
    public void callbackFailed(String msg) {

    }
}

----------------------------------------------------------------------------------------------

基类BaseActivity:

package com.example.zer.text01.Base;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

import com.example.zer.text01.presenter.BasePresenter;
import com.example.zer.text01.view.interfaces.BaseView;

public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements BaseView {
    private P basePresenter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getActivityLayout());
        initView();
        basePresenter=initPresenter();
        basePresenter.attachView(this);
        initData();
        initListener();
    }

    protected P getBasePresenter(){
        return basePresenter;
    }

    protected abstract void initListener();

    protected abstract void initData();

    protected abstract P initPresenter();

    protected abstract void initView();

    protected abstract int getActivityLayout();


}

---------------------------------------------------------------------------------------------------------------------

基类的Presenter:BasePresenter:

package com.example.zer.text01.presenter;

import com.example.zer.text01.view.interfaces.BaseView;

public class BasePresenter<V extends BaseView> {
    private V baseView;

    public void attachView(V baseView){
        this.baseView=baseView;
    }

    public void detachView(){
        if(this.baseView!=null){
            this.baseView=null;
        }
    }

    public V getBaseView(){
        return this.baseView;
    }
}

-----------------------------------------------------------------------------------------------------------------

适配器:MyExpandAdapter:

package com.example.zer.text01.view.adapters;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.example.zer.text01.R;
import com.example.zer.text01.model.bean.ResultBean;
import com.example.zer.text01.view.activitys.CustomChangeNumView;
import com.example.zer.text01.view.activitys.SmoothCheckBox;
import com.example.zer.text01.view.interfaces.UpdateView;

public class MyExpandAdapter extends BaseExpandableListAdapter implements View.OnClickListener {

    private ResultBean resultBean;
    private Context context;
    private UpdateView updateViewListener;

    public MyExpandAdapter(Context context) {
        this.context = context;
    }

    //设置数据
    public void setData(ResultBean rBean){
        this.resultBean=rBean;
    }

    @Override
    public int getGroupCount() {
        return resultBean.getData().size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return resultBean.getData().get(groupPosition).getList().size();
    }

    @Override
    public Object getGroup(int groupPosition) {
        return resultBean.getData().get(groupPosition);
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return resultBean.getData().get(groupPosition).getList().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 groupViewHolder;
        if(convertView==null){
            convertView= LayoutInflater.from(context).inflate(R.layout.groupview,parent,false);
            groupViewHolder=new GroupViewHolder(convertView);
            convertView.setTag(groupViewHolder);
        }else{
            groupViewHolder= (GroupViewHolder) convertView.getTag();
        }
        groupViewHolder.group_name.setText(resultBean.getData().get(groupPosition).getSellerName());
        //设置tag
        groupViewHolder.cb_group.setTag(groupPosition);
        //设置group复选框的点击事件
        groupViewHolder.cb_group.setOnClickListener(this);
        //根据获取到的复选框状态设置是否被选中
        if(resultBean.getData().get(groupPosition).getGroupSelect()){
            if(!groupViewHolder.cb_group.isChecked()){
                groupViewHolder.cb_group.setChecked(true);
            }
        }else{
            groupViewHolder.cb_group.setChecked(false);
        }
        return convertView;
    }

    @Override
    public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        final ChildViewHolder childViewHolder;
        if(convertView==null){
            convertView=LayoutInflater.from(context).inflate(R.layout.childview,parent,false);
            childViewHolder=new ChildViewHolder(convertView);
            convertView.setTag(childViewHolder);
        }else{
            childViewHolder= (ChildViewHolder) convertView.getTag();
        }
        //设置child的tag
        String tag = groupPosition + "," + childPosition;
        childViewHolder.cb_child.setTag(tag);
        childViewHolder.child_rublish.setTag(tag);
        //设置child复选框的点击事件
        childViewHolder.child_rublish.setOnClickListener(this);
        childViewHolder.cb_child.setOnClickListener(this);
        childViewHolder.customChangeNumView.setEditText(resultBean.getData().get(groupPosition).getList().get(childPosition).getNum());
        //加减监听
        childViewHolder.customChangeNumView.setCustomListener(new CustomChangeNumView.CustomListener() {
            @Override
            public void jiajian(int count) {
                resultBean.getData().get(groupPosition).getList().get(childPosition).setNum(count);
                notifyDataSetChanged();
                sumList(resultBean);
            }
        });
        //根据获取的状态设置是否选中
        if(resultBean.getData().get(groupPosition).getList().get(childPosition).getChildSelect()){
            childViewHolder.cb_child.setChecked(true);
        }else{
            childViewHolder.cb_child.setChecked(false);
        }
//        childViewHolder.child_img.setTag(tag);
        if(resultBean.getData().get(groupPosition).getList().get(childPosition).getImages().split("\\|").length>0) {
            Glide.with(context).load(resultBean.getData().get(groupPosition).getList().get(childPosition).getImages().split("\\|")[0]).into(childViewHolder.child_img);
        }else{
            Glide.with(context).load(resultBean.getData().get(groupPosition).getList().get(childPosition).getImages()).into(childViewHolder.child_img);
        }
        childViewHolder.child_title.setText(resultBean.getData().get(groupPosition).getList().get(childPosition).getTitle()+"");
        childViewHolder.child_price.setText(resultBean.getData().get(groupPosition).getList().get(childPosition).getPrice()+"");
        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }

    //的viewholder
    class GroupViewHolder{
        SmoothCheckBox cb_group;
        TextView group_name;
        public GroupViewHolder(View itemView) {
            cb_group=itemView.findViewById(R.id.cb_group);
            group_name=itemView.findViewById(R.id.group_name);
        }
    }

    //child的viewholdergroup
    class ChildViewHolder{
        SmoothCheckBox cb_child;
        ImageView child_img;
        ImageView child_rublish;
        TextView child_title;
        TextView child_price;
        CustomChangeNumView customChangeNumView;


        public ChildViewHolder(View itemView) {
            cb_child=itemView.findViewById(R.id.cb_child);
            child_img=itemView.findViewById(R.id.child_img);
            child_title=itemView.findViewById(R.id.child_title);
            child_price=itemView.findViewById(R.id.child_price);
            child_rublish=itemView.findViewById(R.id.child_rublish);
            customChangeNumView=itemView.findViewById(R.id.custom);
        }
    }

    //点击事件
    @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;

        if(tag.contains(",")){
            split = tag.split(",");
            groupId=Integer.valueOf(split[0]);
            childId=Integer.valueOf(split[1]);
        }else{
            groupPosition=Integer.valueOf(tag);
            childSize=resultBean.getData().get(groupPosition).getList().size();
        }
        switch (v.getId()){
            //组的复选框点击
            case R.id.cb_group:
                checkBox= (SmoothCheckBox) v;
                resultBean.getData().get(groupPosition).setGroupSelect(!checkBox.isChecked());
                if(!checkBox.isChecked()){
                    for (int i=0;i<childSize;i++){
                        if(!resultBean.getData().get(groupPosition).getList().get(i).getChildSelect()){
                            resultBean.getData().get(groupPosition).getList().get(i).setChildSelect(!checkBox.isChecked());
                        }
                    }
                }else{
                    for (int i=0;i<childSize;i++){
                        resultBean.getData().get(groupPosition).getList().get(i).setChildSelect(!checkBox.isChecked());
                    }
                }
                int fCount=0;
                for (int i=0;i<resultBean.getData().size();i++){
                    if(resultBean.getData().get(i).getGroupSelect()){
                        fCount++;
                    }
                }
                if(fCount==resultBean.getData().size()){
                    resultBean.setAllSelect(true);
                }else{
                    resultBean.setAllSelect(false);
                }
                sumList(resultBean);
                notifyDataSetChanged();
                break;
            case R.id.cb_child:
                checkBox= (SmoothCheckBox) v;
                int cCount=0;
                int gCount=0;
                resultBean.getData().get(groupId).getList().get(childId).setChildSelect(!checkBox.isChecked());
                for (int i=0;i<resultBean.getData().get(groupId).getList().size();i++){
                    if(resultBean.getData().get(groupId).getList().get(i).getChildSelect()){
                        cCount++;
                    }
                }
                if(cCount==resultBean.getData().get(groupId).getList().size()){
                    resultBean.getData().get(groupId).setGroupSelect(true);
                }else{
                    resultBean.getData().get(groupId).setGroupSelect(false);
                }

                for (int i=0;i<resultBean.getData().size();i++){
                    if(resultBean.getData().get(i).getGroupSelect()){
                        gCount++;
                    }
                }

                if(gCount==resultBean.getData().size()){
                    resultBean.setAllSelect(true);
                }else{
                    resultBean.setAllSelect(false);
                }
                sumList(resultBean);
                notifyDataSetChanged();
                break;
            case R.id.child_rublish:
                resultBean.getData().get(groupId).getList().remove(childId);
                if(resultBean.getData().get(groupId).getList().size()==0){
                    resultBean.getData().remove(groupId);
                }
                sumList(resultBean);
                notifyDataSetChanged();
                break;
        }
    }

    //设置updateviewlistener
    public void setUpdateViewListener(UpdateView Listener){
        if(updateViewListener==null){
            this.updateViewListener=Listener;
        }
    }

    //计算总价的
    public void sumList(ResultBean resultBean){
        int allCount=0;
        float allMoney=0;

        for (int i=0;i<resultBean.getData().size();i++){
            for(int j=0;j<resultBean.getData().get(i).getList().size();j++){
                if(resultBean.getData().get(i).getList().get(j).getChildSelect()){
                    allCount+=resultBean.getData().get(i).getList().get(j).getNum();
                    allMoney+=resultBean.getData().get(i).getList().get(j).getNum()*resultBean.getData().get(i).getList().get(j).getPrice();
                }
            }
        }
        resultBean.setAllCount(allCount);
        resultBean.setAllMoney(allMoney);
        updateViewListener.update(resultBean.getAllSelect(),allCount,allMoney);
    }

    //全选
    public void selectAll(boolean checked){
        resultBean.setAllSelect(checked);
        for (int i=0;i<resultBean.getData().size();i++){
            resultBean.getData().get(i).setGroupSelect(checked);
            for (int j=0;j<resultBean.getData().get(i).getList().size();j++){
                resultBean.getData().get(i).getList().get(j).setChildSelect(checked);
            }
        }
        notifyDataSetChanged();
        sumList(resultBean);
    }

}

MainActivity的接口:IMainView:

public interface IMainView extends BaseView {
    void getBackData(ResultBean resultBean);
}

基类的接口(BaseView)没有任何东西,在这里就不写啦^_^

--------------------------------------------------------------------------------------------------------------

UpdateView:

package com.example.zer.text01.view.interfaces;

public interface UpdateView {
    void update(boolean isAllSelected, int count, float price);
}

------------------------------------------------------------------------------------------------------

自定义view:CustomChangeNumView

package com.example.zer.text01.view.activitys;

import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import com.example.zer.text01.R;

public class CustomChangeNumView extends RelativeLayout{

    private Button reverse;
    private Button add;
    private EditText countEdit;
    private CustomListener customListener;

    public CustomChangeNumView(Context context) {
        super(context);
        initView(context);
    }

    public CustomChangeNumView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public CustomChangeNumView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    public void initView(Context context) {
        View view= View.inflate(context, R.layout.changeview,null);

        reverse = (Button) view.findViewById(R.id.child_reverse);

        add = (Button) view.findViewById(R.id.child_add);

        countEdit = (EditText) view.findViewById(R.id.child_count);
        //减的时候回调
        reverse.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                int count = Integer.valueOf(countEdit.getText().toString());
                if(count>1){
                    count--;
                    countEdit.setText(count+"");
                    if(customListener!=null){
                        customListener.jiajian(count);
                    }
                }
            }
        });
        //加的时候回调
        add.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                int count = Integer.valueOf(countEdit.getText().toString());
                count++;
                countEdit.setText(count+"");
                if(customListener!=null){
                    customListener.jiajian(count);
                }
            }
        });

        this.addView(view);
    }

    //设置接口
    public interface CustomListener{
        void jiajian(int count);
    }

    //设置回调方法
    public void setCustomListener(CustomListener customListener){
        this.customListener=customListener;
    }

    public void setEditText(int num) {
        if(countEdit !=null) {
            countEdit.setText(num + "");
        }
    }
}

-----------------------------------------------------------------------------------------------------

组合控件:SmoothCheckBox(复选框)

package com.example.zer.text01.view.activitys;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Path;
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;

import com.example.zer.text01.R;
import com.example.zer.text01.util.TextSizeUtils;

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);
    }

}

TextSizeUtils(文字大小):

package com.example.zer.text01.util;

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);
    }
}

--------------------------------------------------------------------------------------------------------------------

DataBack:

package com.example.zer.text01.presenter;

import com.example.zer.text01.model.bean.ResultBean;

public interface DataBack {
    void callbackSuccess(ResultBean resultBean);
    void callbackFailed(String msg);
}

----------------------------------------------------------------------------------------------------------

HttpInterceptor:

package com.example.zer.text01.model.net;

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

class HttpInterceptor implements Interceptor {
    private static final String s = "source";
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        String url = request.url().toString()+"?source=android";
        Request newrequest=request.newBuilder()
                .url(url)
                .build();
        Response response = chain.proceed(newrequest);
        return response;
    }
}

-------------------------------------------------------------------------------------------------------

MainMoudle:

package com.example.zer.text01.model.net;

import com.example.zer.text01.model.bean.ResultBean;
import com.example.zer.text01.presenter.DataBack;
import com.google.gson.Gson;

import java.io.IOException;
import java.util.Map;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;

public class MainMoudle {
    //获取手机数据
    public void getShopCarData(String url, Map<String,String> parms, final DataBack dataBack){
        OkHttpUtil.getInstance().doPost(url, parms, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String json = response.body().string();
                Gson gson=new Gson();
                ResultBean resultBean = gson.fromJson(json, ResultBean.class);
                dataBack.callbackSuccess(resultBean);

            }
        });
    }
}

-------------------------------------------------------------------------------------------------------

OKhttpUtil:

package com.example.zer.text01.model.net;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;

public class OkHttpUtil {
    static OkHttpUtil okHttpUtil;
    static OkHttpClient okHttpClient;

    private OkHttpUtil() {

    }

    public static OkHttpUtil getInstance(){
        if(okHttpUtil==null){
            synchronized (OkHttpUtil.class){
                if(okHttpUtil==null){
                    okHttpUtil=new OkHttpUtil();
                    okHttpClient=new OkHttpClient.Builder()
                            .readTimeout(5000, TimeUnit.SECONDS)
                            .addInterceptor(new HttpInterceptor())
                            .connectTimeout(5000,TimeUnit.SECONDS)
                            .build();
                }
            }
        }
        return okHttpUtil;
    }

    //get请求
    public void doGet(String url, Callback callback){
        Request request=new Request.Builder()
                .url(url)
                .build();
        okHttpClient.newCall(request).enqueue(callback);
    }

    //post
    public void doPost(String url, Map<String,String> parms, Callback callback){
        FormBody.Builder builder=new FormBody.Builder();
        Iterator<String> iterator = parms.keySet().iterator();
        while (iterator.hasNext()){
            String key = iterator.next();
            String value = parms.get(key);
            builder.add(key,value);
        }
        Request request=new Request.Builder()
                .url(url)
                .post(builder.build())
                .build();
        okHttpClient.newCall(request).enqueue(callback);
    }
}

------------------------------------------------------------------------------------------------------

attrs.xml

<?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>


猜你喜欢

转载自blog.csdn.net/grace_er/article/details/80163651
今日推荐