布局文件: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>