mac
2024-03-07 0a13476751c01ae346c30a4c4dd266bad700d2c3
2024年03月07日13:28:38

备份代码
7个文件已添加
9个文件已修改
2309 ■■■■■ 已修改文件
app/src/main/java/com/hdl/photovoltaic/HDLApp.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/other/HdlOtaLogic.java 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/other/HdlResidenceLogic.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/ui/adapter/HouseInfoAdapter.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/ui/bean/HouseIdBean.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/ui/powerstation/HouseListFragment.java 141 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/widget/DelayedConfirmationCancelDialog.java 241 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/widget/SwipeLayout.java 1557 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/del_house.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/editor_house.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/move_house.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/frgment_house_list_line.xml 147 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/swipe_right_layout.xml 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/toolbar_top_view_52.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/values/styles.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/values/values.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/hdl/photovoltaic/HDLApp.java
@@ -61,14 +61,13 @@
    @Override
    public void onCreate() {
        super.onCreate();
        mHDLApp = this;
        SharedPreUtils.init(this);
        getAppLanguage();
        initThirdSDK();
//        appLocationInfo();
    }
    public void getAppLanguage() {
app/src/main/java/com/hdl/photovoltaic/other/HdlOtaLogic.java
@@ -536,12 +536,12 @@
        //-100:没有数据;-101:没有md5秘钥;-102:md5秘钥不对;-103:有异常;
        if (zipData == null) {
            this.eventBusPost(fileFullPath, driver, -100, HDLApp.getInstance().getString(R.string.exception_unable_to_pull_up_the_cloud_upgrade_file_data));
            HdlLogLogic.print("拉不到云端升级文件数据(-100)", true);
//            HdlLogLogic.print("拉不到云端升级文件数据(-100)", true);
            return false;
        }
        if (TextUtils.isEmpty(md5)) {
            this.eventBusPost(fileFullPath, driver, -101, HDLApp.getInstance().getString(R.string.exception_failed_to_obtain_the_md5_key_of_the_upgrade_file_on_the_cloud));
            HdlLogLogic.print("拿不到云端上升级文件md5秘钥(-101)", true);
//            HdlLogLogic.print("拿不到云端上升级文件md5秘钥(-101)", true);
            return false;
        }
        InputStream is = null;
@@ -555,7 +555,7 @@
            long total = zipData.contentLength();
            if (total == 0) {
                this.eventBusPost(fileFullPath, driver, -102, HDLApp.getInstance().getString(R.string.exception_unable_to_pull_up_the_cloud_upgrade_file_data));
                HdlLogLogic.print("拉不到云端升级文件数据(-102)", true);
//                HdlLogLogic.print("拉不到云端升级文件数据(-102)", true);
                return false;
            }
            fos = new FileOutputStream(file);
@@ -563,7 +563,7 @@
            while ((len = is.read(buf)) != -1) {
                if (this.stopDownload) {
                    this.eventBusPost(fileFullPath, driver, -108, HDLApp.getInstance().getString(R.string.exception_the_user_cancels_downloading_the_upgrade_file));
                    HdlLogLogic.print("用户取消下载升级文件(-108)", true);
//                    HdlLogLogic.print("用户取消下载升级文件(-108)", true);
                    fos.flush();
                    fos.close();
                    is.close();
@@ -576,36 +576,37 @@
                int progress = (int) (100 * sum * 1.0f / total);
                if (sum != total) {
                    //不等于100都要报,等于100处理整块逻辑完之后再报100;
                    this.eventBusPost(fileFullPath, driver, progress, "正常上报进度值->" + progress);
                    HdlLogLogic.print("正常上报进度值->" + progress, true);
                    this.eventBusPost(fileFullPath, driver, progress, "正常上报进度值--->" + progress);
//                    HdlLogLogic.print("正常上报进度值--->" + progress, true);
                }
            }
            fos.flush();
            fos.close();
            is.close();
            byte[] bytes = FileUtils.readFileToByteArray(file);
            byte[] bytes = HdlFileLogic.getInstance().readFileByte(fileFullPath);
//            byte[] bytes = FileUtils.readFileToByteArray(file);//这个方法不兼用android 6.0
            //todo 下载文件需要解密之后再进行md5去做比对
            byte[] decrypt = AesUtils.decrypt(bytes);
            if (decrypt == null) {
                this.eventBusPost(fileFullPath, driver, -105, HDLApp.getInstance().getString(R.string.exception_failed_to_decrypt_the_upgrade_aes_file));
                HdlLogLogic.print("升级文件aes解密失败(-105)", true);
//                HdlLogLogic.print("升级文件aes解密失败(-105)", true);
                return false;
            }
            String fileMD5 = Md5Utils.encodeMD5(decrypt);
            if (TextUtils.isEmpty(fileMD5)) {
                this.eventBusPost(fileFullPath, driver, -106, HDLApp.getInstance().getString(R.string.exception_failed_to_generate_md5_for_the_upgrade_file));
                HdlLogLogic.print("升级文件生成md5失败失败(-106)", true);
//                HdlLogLogic.print("升级文件生成md5失败失败(-106)", true);
                return false;
            }
            if (!md5.equals(fileMD5)) {
                this.eventBusPost(fileFullPath, driver, -107, HDLApp.getInstance().getString(R.string.exception_description_failed_to_compare_the_md5_of_the_upgrade_file));
                HdlLogLogic.print("升级文件md5比对失败(-107)", true);
//                HdlLogLogic.print("升级文件md5比对失败(-107)", true);
                return false;
            }
            //todo 注意:解密之后,要重新写数据;
            FileUtils.writeByteArrayToFile(file, decrypt, false);
            HdlFileLogic.getInstance().writeFile(fileFullPath, decrypt);
//            FileUtils.writeByteArrayToFile(file, decrypt, false);//这个方法不兼用android 6.0
            this.eventBusPost(fileFullPath, driver, 100, HDLApp.getInstance().getString(R.string.exception_the_upgrade_file_is_downloaded));
            HdlLogLogic.print("升级文件下载完成.", true);
            return true;
        } catch (Exception e) {
            this.eventBusPost(fileFullPath, driver, -103, e.getMessage());
app/src/main/java/com/hdl/photovoltaic/other/HdlResidenceLogic.java
@@ -28,6 +28,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -89,6 +90,48 @@
        this.houseIdList = houseIdList;
    }
    /**
     * 删除电站
     *
     * @param houseId 电站id
     */
    public void delHouseId(String houseId) {
        if (TextUtils.isEmpty(houseId)) {
            return;
        }
        for (int i = 0; i < houseIdList.size(); i++) {
            if (houseIdList.get(i).getHomeId().equals(houseId)) {
                houseIdList.remove(i);
                break;
            }
        }
    }
    /**
     * 移动电站位置
     *
     * @param houseId 电站id
     */
    public void moveHouseId(String houseId) {
        if (TextUtils.isEmpty(houseId)) {
            return;
        }
        int index = -1;
        for (int i = 0; i < houseIdList.size(); i++) {
            if (houseIdList.get(i).getHomeId().equals(houseId)) {
                index = i;
                break;
            }
        }
        if (index == -1) {
            return;
        }
        //index==0表示只有一个元素或者在首位,不需要移动位置
        if (index > 0) {
            Collections.swap(houseIdList, index - 1, index);
        }
    }
    //住宅ID列表
    private List<HouseIdBean> houseIdList = new ArrayList<>();
    //住宅详情列表
app/src/main/java/com/hdl/photovoltaic/ui/adapter/HouseInfoAdapter.java
@@ -1,12 +1,12 @@
package com.hdl.photovoltaic.ui.adapter;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -15,8 +15,10 @@
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.hdl.photovoltaic.R;
import com.hdl.photovoltaic.other.HdlLogLogic;
import com.hdl.photovoltaic.ui.bean.HouseIdBean;
import com.hdl.photovoltaic.utils.GlideUtils;
import com.hdl.photovoltaic.widget.SwipeLayout;
import java.util.ArrayList;
import java.util.List;
@@ -26,6 +28,11 @@
    private List<HouseIdBean> mList;
    private final Context mContext;
    private OnclickListener noOnclickListener;//点击了的监听器
    /**
     * 收集SwipeLayout数组,要一键全部打开;
     */
    private List<SwipeLayout> swipeLayoutList = new ArrayList<>();
    public HouseInfoAdapter(Context context) {
@@ -41,6 +48,7 @@
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        HouseIdBean houseIdBean = this.mList.get(position);
        holder.homeNameTv.setText(houseIdBean.getHomeName().trim());
@@ -63,15 +71,57 @@
        //发电功率
        String power = mContext.getString(R.string.power_station_generated_power) + kw + "kW";
        holder.powerTv.setText(power);
        holder.itemView.setTag(position);
        holder.item_parent_rl.setTag(position);
        setTextViewStyle(holder.stateTv, houseIdBean.getPowerStationStatus());
        GlideUtils.getRoundedCornersImage(mContext, houseIdBean.getPowerStationImage(), holder.homeIconIv, new RoundedCorners(4));
        holder.itemView.setOnClickListener(new View.OnClickListener() {
        if (houseIdBean.isEdit()) {
            holder.item_parent_swipeLayout.postDelayed(new Runnable() {
                @Override
                public void run() {
                    holder.item_parent_swipeLayout.open();
                }
            }, 200);
        } else {
            holder.item_parent_swipeLayout.postDelayed(new Runnable() {
                @Override
                public void run() {
                    holder.item_parent_swipeLayout.close();
                }
            }, 200);
        }
        HdlLogLogic.print("电站名称--->" + houseIdBean.getHomeName() + "--->" + houseIdBean.isEdit() + "--->" + holder.item_parent_swipeLayout.toString());
        holder.item_parent_rl.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if (noOnclickListener != null) {
                        noOnclickListener.onClick((int) holder.itemView.getTag(), houseIdBean);
                        noOnclickListener.onClick((int) holder.item_parent_rl.getTag(), houseIdBean);
                    }
                } catch (Exception ignored) {
                }
            }
        });
        //移动电站位置
        holder.move_home_iv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if (noOnclickListener != null) {
                        noOnclickListener.onMoveClick((int) holder.item_parent_rl.getTag(), houseIdBean);
                    }
                } catch (Exception ignored) {
                }
            }
        });
        //删除电站
        holder.del_home_iv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if (noOnclickListener != null) {
                        noOnclickListener.onDelClick((int) holder.item_parent_rl.getTag(), houseIdBean);
                    }
                } catch (Exception ignored) {
                }
@@ -97,8 +147,16 @@
        notifyDataSetChanged();
    }
    public void setNoOnclickListener(OnclickListener onclickListener) {
        this.noOnclickListener = onclickListener;
    }
    public List<SwipeLayout> getSwipeLayoutList() {
        if (this.swipeLayoutList == null) {
            this.swipeLayoutList = new ArrayList<>();
        }
        return this.swipeLayoutList;
    }
    /**
@@ -152,6 +210,10 @@
        public TextView capacityTv;//装机容量
        public TextView powerTv;//发电功率
        public TextView stateTv;//电站状态(连接中,运行,离线,故障);
        public RelativeLayout item_parent_rl;//条目父容器
        public ImageView move_home_iv;//移动电站位置
        public ImageView del_home_iv;//删除电站
        public SwipeLayout item_parent_swipeLayout;//父容器
        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
@@ -160,10 +222,20 @@
            capacityTv = itemView.findViewById(R.id.fragment_house_list_line_capacity_tv);
            powerTv = itemView.findViewById(R.id.fragment_house_list_line_power_tv);
            stateTv = itemView.findViewById(R.id.fragment_house_list_line_state_tv);
            item_parent_rl = itemView.findViewById(R.id.item_parent_rl);
            move_home_iv = itemView.findViewById(R.id.move_home_iv);
            del_home_iv = itemView.findViewById(R.id.del_home_iv);
            item_parent_swipeLayout = itemView.findViewById(R.id.item_parent_swipeLayout);
        }
    }
    public interface OnclickListener {
        void onClick(int position, HouseIdBean houseIdBean);
        void onMoveClick(int position, HouseIdBean houseIdBean);
        void onDelClick(int position, HouseIdBean houseIdBean);
    }
}
app/src/main/java/com/hdl/photovoltaic/ui/bean/HouseIdBean.java
@@ -14,9 +14,20 @@
    private String installedCapacity;//装机容量
    private String power;//发电功率
    private String todayElectricity;//今天发电量
    private int powerStationStatus;//电站状态(电站状态    1:正常(运行),2:离线,3:连接中,4:故障)
    private String localSecret;//本地通讯秘钥
    private boolean isEdit = false;//是否移除电站位置(true表示移除)
    public boolean isEdit() {
        return isEdit;
    }
    public void setEdit(boolean edit) {
        isEdit = edit;
    }
    public String getHomeId() {
        return homeId == null ? "" : homeId;
    }
app/src/main/java/com/hdl/photovoltaic/ui/powerstation/HouseListFragment.java
@@ -1,32 +1,20 @@
package com.hdl.photovoltaic.ui.powerstation;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Movie;
import android.hardware.camera2.CameraManager;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.load.resource.gif.GifDrawable;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.google.gson.Gson;
import com.hdl.linkpm.sdk.core.exception.HDLException;
import com.hdl.photovoltaic.R;
import com.hdl.photovoltaic.config.AppConfigManage;
import com.hdl.photovoltaic.config.ConstantManage;
import com.hdl.photovoltaic.config.UserConfigManage;
import com.hdl.photovoltaic.databinding.FragmentHouseListBinding;
import com.hdl.photovoltaic.base.CustomBaseFragment;
import com.hdl.photovoltaic.enums.HomepageTitleTabSwitch;
@@ -40,9 +28,7 @@
import com.hdl.photovoltaic.ui.adapter.HouseInfoAdapter;
import com.hdl.photovoltaic.ui.bean.HouseIdBean;
import com.hdl.photovoltaic.uni.HDLUniMP;
import com.hdl.photovoltaic.utils.GlideUtils;
import com.hdl.photovoltaic.utils.PermissionUtils;
import com.hdl.photovoltaic.widget.MovieDrawable;
import com.hdl.sdk.link.core.bean.eventbus.BaseEventBus;
import com.hdl.sdk.link.core.utils.mqtt.MqttRecvClient;
@@ -53,15 +39,7 @@
 * (住宅)电站列表-界面
 */
public class HouseListFragment extends CustomBaseFragment {
    public static final String SORT_TYPE_1 = "powerSort";//发电功率排序
    public static final String SORT_TYPE_2 = "todayElectricitySort";//今日发电量排序
    public static final String SORT_TYPE_3 = "createTimeSort";//创建时间排序
    public static final String DESCENDING = "descending";//降序
    public static final String ASCENDING = "ascending";//升序
    public static final String SELECTED_SORT_TYPE = SORT_TYPE_1;
    public static final String SELECTED_SORT = DESCENDING;
    private boolean is_edit = false;
    private FragmentHouseListBinding viewBinding;
    private HouseInfoAdapter houseInfoAdapter;
    private CameraManager manager;
@@ -87,14 +65,23 @@
    private void initEvent() {
        viewBinding.toolbarTopFragmentHouseListRl.topBackBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (is_edit) {
                    is_edit = false;
                }
                viewBinding.toolbarTopFragmentHouseListRl.topBackBtn.setVisibility(View.GONE);
                initData();//初始化缓存数据
                setAllItemEdit(false);
                houseInfoAdapter.setList(houseListBeanIDList);//重新刷新列表
            }
        });
        //添加电站
        viewBinding.toolbarTopFragmentHouseListRl.topMoreIv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                HdlUniLogic.getInstance().openUniMP(HDLUniMP.UNI_EVENT_OPEN_HOME_CREATION, null);
//                requestPermissions(new PermissionsResultCallback() {
//                    @Override
//                    public void succeed() {
@@ -119,10 +106,11 @@
        viewBinding.fragmentHouseSrl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                updateUIData(true);
                downReadData(true);
                HdlLogLogic.print("HouseListFragment下拉刷新", false);
            }
        });
        //进入电站详情,删除电站,移动电站位置
        houseInfoAdapter.setNoOnclickListener(new HouseInfoAdapter.OnclickListener() {
            @Override
            public void onClick(int position, HouseIdBean houseIdBean) {
@@ -136,11 +124,88 @@
                HdlUniLogic.getInstance().openUniMP(path, null);
            }
            @Override
            public void onMoveClick(int position, HouseIdBean houseIdBean) {
                if (position > 0) {
                    houseInfoAdapter.notifyItemMoved(position - 1, position);
                }
//                //移动电站位置
//                HdlResidenceLogic.getInstance().moveHouseId(houseIdBean.getHomeId());
//                initData();//初始化缓存数据
//                houseInfoAdapter.setList(houseListBeanIDList);//重新刷新列表
//                nullDataUpdateUi(houseListBeanIDList);//检测数据是否为空
            }
            @Override
            public void onDelClick(int position, HouseIdBean houseIdBean) {
                HdlResidenceLogic.getInstance().delHouseId(houseIdBean.getHomeId());
                initData();//初始化缓存数据
                houseInfoAdapter.notifyItemRemoved(position);
//                DelayedConfirmationCancelDialog delayedConfirmationCancelDialog = new DelayedConfirmationCancelDialog(_mActivity);
//                delayedConfirmationCancelDialog.setTitle(R.string.loading_title_tip);
//                delayedConfirmationCancelDialog.setContent("是否确认删除电站?");
//                delayedConfirmationCancelDialog.show();
//                delayedConfirmationCancelDialog.startCountdown(3);
//                delayedConfirmationCancelDialog.setYesOnclickListener(new ConfirmationCancelDialog.onYesOnclickListener() {
//                    @Override
//                    public void Confirm() {
//                        //删除住宅
//                        HdlResidenceLogic.getInstance().delResidence(houseIdBean.getHomeId(), new CloudCallBeak<Boolean>() {
//                            @Override
//                            public void onSuccess(Boolean obj) {
//                                HdlResidenceLogic.getInstance().delHouseId(houseIdBean.getHomeId());
//                                initData();//初始化缓存数据
//                                setAllItemEdit(is_edit);
//                                houseInfoAdapter.setList(houseListBeanIDList);//重新刷新列表
//                                nullDataUpdateUi(houseListBeanIDList);//检测数据是否为空
//                                delayedConfirmationCancelDialog.dismiss();
//                            }
//
//                            @Override
//                            public void onFailure(HDLException e) {
//                                HdlThreadLogic.toast(_mActivity, e);
//                            }
//                        });
//                    }
//                });
//                delayedConfirmationCancelDialog.setNoOnclickListener(new ConfirmationCancelDialog.onNoOnclickListener() {
//                    @Override
//                    public void Cancel() {
//                        delayedConfirmationCancelDialog.dismiss();
//                    }
//                });
            }
        });
        //编辑电站按钮
        viewBinding.toolbarTopFragmentHouseListRl.topEditIv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (is_edit) {
                    //防止多次点击
                    return;
                }
                is_edit = true;
                viewBinding.toolbarTopFragmentHouseListRl.topBackBtn.setVisibility(View.VISIBLE);
                if (houseInfoAdapter == null) {
                    return;
                }
                initData();//初始化缓存数据
                setAllItemEdit(true);
                houseInfoAdapter.setList(houseListBeanIDList);//重新刷新列表
            }
        });
    }
    private void initView() {
        viewBinding.toolbarTopFragmentHouseListRl.topTitleTv.setText(R.string.my_power_station);
        viewBinding.toolbarTopFragmentHouseListRl.topEditIv.setVisibility(View.VISIBLE);
        viewBinding.toolbarTopFragmentHouseListRl.topEditIv.setImageResource(R.drawable.editor_house);
        viewBinding.toolbarTopFragmentHouseListRl.topMoreIv.setVisibility(View.VISIBLE);
        viewBinding.toolbarTopFragmentHouseListRl.topMoreIv.setImageResource(R.drawable.add);
        LinearLayoutManager linearLayout = new LinearLayoutManager(_mActivity);
@@ -169,7 +234,7 @@
        if (HDLUniMP.UNI_EVENT_REPLY_HOME_MODEL.equals(eventBus.getTopic())) {
            if (HDLUniMP.UNI_EVENT_REPLY_HOME_CREATION.equals(eventBus.getType())) {
                //uin创建电站成功后通知
                updateUIData(false);
                downReadData(false);
                if (eventBus.getData() != null) {
                    Gson gson = new Gson();
                    String json = eventBus.getData().toString();
@@ -208,7 +273,7 @@
            if (MqttRecvClient.getInstance() != null) {
                MqttRecvClient.getInstance().removeAllTopic();
            }
            updateUIData(false);
            downReadData(false);
        } else if (HDLUniMP.UNI_EVENT_REPLY_DEVICE_LIST.equals(eventBus.getType())) {
            //进去住宅详情uni读取逆变器列表成功后通知
@@ -227,12 +292,24 @@
        }
    }
    /**
     * 设置全部缓存数据编辑状态
     *
     * @param edit true表示在编辑状态
     */
    private void setAllItemEdit(boolean edit) {
        for (int i = 0; i < houseListBeanIDList.size(); i++) {
            houseListBeanIDList.get(i).setEdit(edit);
        }
    }
    /**
     * 刷新UI
     *
     * @param isRefreshing 表示是下拉刷新的
     */
    private void updateUIData(boolean isRefreshing) {
    private void downReadData(boolean isRefreshing) {
        //获取住宅(电站)ID列表
        HdlResidenceLogic.getInstance().getResidenceIdList("", "", new CloudCallBeak<List<HouseIdBean>>() {
            @Override
@@ -248,8 +325,10 @@
                            //更新缓存
                            HdlResidenceLogic.getInstance().setHouseIdList(list);
                            if (houseInfoAdapter != null) {
                                initData();
                                setAllItemEdit(is_edit);
                                //更新UI
                                houseInfoAdapter.setList(list);
                                houseInfoAdapter.setList(houseListBeanIDList);
                            }
                        }
app/src/main/java/com/hdl/photovoltaic/widget/DelayedConfirmationCancelDialog.java
New file
@@ -0,0 +1,241 @@
package com.hdl.photovoltaic.widget;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.text.TextUtils;
import android.view.View;
import androidx.annotation.NonNull;
import com.hdl.photovoltaic.R;
import com.hdl.photovoltaic.databinding.DialogConfirmCancelBinding;
/**
 * 延时确认框
 */
public class DelayedConfirmationCancelDialog extends Dialog {
    public DelayedConfirmationCancelDialog(@NonNull Context context) {
        super(context, R.style.Custom_Dialog);
        this.mContext = context;
    }
    CountDownTimer timer;
    private final Context mContext;
    private ConfirmationCancelDialog.onNoOnclickListener noOnclickListener;//取消按钮被点击了的监听器
    private ConfirmationCancelDialog.onYesOnclickListener yesOnclickListener;//确定按钮被点击了的监听器
    private DialogConfirmCancelBinding viewBinding;
    private String titleStr, contentStr, yesStr, noStr;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewBinding = DialogConfirmCancelBinding.inflate(getLayoutInflater());
        setContentView(viewBinding.getRoot());
//        setCancelable(true);//系统后退可以取消
        //空白处不能取消动画
        setCanceledOnTouchOutside(false);
        //初始化界面控件
        initView();
        //初始化界面数据
        initData();
        //初始化界面控件的事件
        initEvent();
    }
    /**
     * 自定义"标题"文本
     *
     * @param title 内容
     */
    public void setTitle(String title) {
        if (TextUtils.isEmpty(title)) {
            return;
        }
        titleStr = title;
        if (viewBinding != null) {
            viewBinding.loadingConfirmationTitleTv.setText(titleStr);
        }
    }
    /**
     * 自定义"确认"文本
     *
     * @param confirm 内容
     */
    public void setConfirmation(String confirm) {
        if (TextUtils.isEmpty(confirm)) {
            return;
        }
        yesStr = confirm;
        if (viewBinding != null) {
            viewBinding.dialogConfirmTv.setText(yesStr);
        }
    }
    /**
     * 自定义"确认"文本颜色
     */
    public void setConfirmationColor(int color) {
        if (viewBinding != null) {
            viewBinding.dialogConfirmTv.setTextColor(color);
        }
    }
    /**
     * 自定义"取消"文本
     *
     * @param cancel 内容
     */
    public void setCancel(String cancel) {
        if (TextUtils.isEmpty(cancel)) {
            return;
        }
        noStr = cancel;
        if (viewBinding != null) {
            viewBinding.dialogCancelTv.setText(noStr);
        }
    }
    /**
     * 自定义"内容"文本
     *
     * @param content 内容
     */
    public void setContent(String content) {
        if (TextUtils.isEmpty(content)) {
            return;
        }
        contentStr = content;
        if (viewBinding != null) {
            viewBinding.loadingConfirmationContentTv.setText(contentStr);
        }
    }
    private void initEvent() {
        viewBinding.dialogCancelLy.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (noOnclickListener != null) {
                    noOnclickListener.Cancel();
                }
            }
        });
        viewBinding.dialogConfirmLy.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (yesOnclickListener != null) {
                    yesOnclickListener.Confirm();
                }
            }
        });
    }
    /**
     * 初始化界面控件的显示数据
     */
    private void initData() {
        //如果用户自定了title和message
        if (!TextUtils.isEmpty(titleStr)) {
            viewBinding.loadingConfirmationTitleTv.setText(titleStr);
        }
        if (!TextUtils.isEmpty(contentStr)) {
            viewBinding.loadingConfirmationContentTv.setText(contentStr);
        }
        //如果设置按钮文字
        if (!TextUtils.isEmpty(yesStr)) {
            viewBinding.dialogConfirmTv.setText(yesStr);
        }
        if (!TextUtils.isEmpty(noStr)) {
            viewBinding.dialogCancelTv.setText(noStr);
        }
    }
    private void initView() {
    }
    /**
     * 开始倒计时
     *
     * @param seconds 表示多少秒
     */
    public void startCountdown(long seconds) {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
        timer = new CountDownTimer(seconds * 1000, 1000) { // 设置倒计时为3秒(单位:毫秒)
            public void onTick(long millisUntilFinished) {
                int secondsRemaining = (int) (millisUntilFinished / 1000);
                String timeLeft = "(" + secondsRemaining + ")";
                String text = mContext.getString(R.string.loading_affirm) + timeLeft;
                viewBinding.dialogConfirmTv.setText(text);
                viewBinding.dialogConfirmTv.setTextColor(Color.parseColor("#C2EDDE"));
                viewBinding.dialogConfirmLy.setEnabled(false);
                viewBinding.dialogConfirmTv.setEnabled(false);
            }
            public void onFinish() {
                viewBinding.dialogConfirmTv.setText(mContext.getString(R.string.loading_affirm));
                viewBinding.dialogConfirmTv.setTextColor(Color.parseColor("#38C494"));
                viewBinding.dialogConfirmLy.setEnabled(true);
                viewBinding.dialogConfirmTv.setEnabled(true);
                if (timer != null) {
                    timer.cancel();
                    timer = null;
                }
            }
        };
        timer.start();
    }
    /**
     * 设置取消按钮监听
     *
     * @param onNoOnclickListener -
     */
    public void setNoOnclickListener(ConfirmationCancelDialog.onNoOnclickListener onNoOnclickListener) {
        if (onNoOnclickListener != null) {
            this.noOnclickListener = onNoOnclickListener;
        }
    }
    /**
     * 设置确定按钮监听
     *
     * @param yesOnclickListener -
     */
    public void setYesOnclickListener(ConfirmationCancelDialog.onYesOnclickListener yesOnclickListener) {
        if (yesOnclickListener != null) {
            this.yesOnclickListener = yesOnclickListener;
        }
    }
    public interface onNoOnclickListener {
        void Cancel();
    }
    public interface onYesOnclickListener {
        void Confirm();
    }
}
app/src/main/java/com/hdl/photovoltaic/widget/SwipeLayout.java
New file
@@ -0,0 +1,1557 @@
package com.hdl.photovoltaic.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import androidx.core.view.GravityCompat;
import androidx.core.view.ViewCompat;
import androidx.customview.widget.ViewDragHelper;
import com.hdl.photovoltaic.R;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class SwipeLayout extends FrameLayout {
    public static final int EMPTY_LAYOUT = -1;
    private static final int DRAG_LEFT = 1;
    private static final int DRAG_RIGHT = 2;
    private static final int DRAG_TOP = 4;
    private static final int DRAG_BOTTOM = 8;
    private static final DragEdge DefaultDragEdge = DragEdge.Right;
    private int mTouchSlop;
    private DragEdge mCurrentDragEdge = DefaultDragEdge;
    private ViewDragHelper mDragHelper;
    private int mDragDistance = 0;
    private LinkedHashMap<DragEdge, View> mDragEdges = new LinkedHashMap<DragEdge, View>();
    private ShowMode mShowMode;
    private float[] mEdgeSwipesOffset = new float[4];
    private List<SwipeListener> mSwipeListeners = new ArrayList<SwipeListener>();
    private List<SwipeDenier> mSwipeDeniers = new ArrayList<SwipeDenier>();
    private Map<View, ArrayList<OnRevealListener>> mRevealListeners = new HashMap<View, ArrayList<OnRevealListener>>();
    private Map<View, Boolean> mShowEntirely = new HashMap<View, Boolean>();
    private DoubleClickListener mDoubleClickListener;
    private boolean mSwipeEnabled = true;
    private boolean[] mSwipesEnabled = new boolean[]{true, true, true, true};
    private boolean mClickToClose = false;
    public static enum DragEdge {
        Left,
        Top,
        Right,
        Bottom
    }
    public static enum ShowMode {
        LayDown,
        PullOut
    }
    public SwipeLayout(Context context) {
        this(context, null);
    }
    public SwipeLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public SwipeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mDragHelper = ViewDragHelper.create(this, mDragHelperCallback);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeLayout);
        int dragEdgeChoices = a.getInt(R.styleable.SwipeLayout_drag_edge, DRAG_RIGHT);
        mEdgeSwipesOffset[DragEdge.Left.ordinal()] = a.getDimension(R.styleable.SwipeLayout_leftEdgeSwipeOffset, 0);
        mEdgeSwipesOffset[DragEdge.Right.ordinal()] = a.getDimension(R.styleable.SwipeLayout_rightEdgeSwipeOffset, 0);
        mEdgeSwipesOffset[DragEdge.Top.ordinal()] = a.getDimension(R.styleable.SwipeLayout_topEdgeSwipeOffset, 0);
        mEdgeSwipesOffset[DragEdge.Bottom.ordinal()] = a.getDimension(R.styleable.SwipeLayout_bottomEdgeSwipeOffset, 0);
        setClickToClose(a.getBoolean(R.styleable.SwipeLayout_clickToClose, mClickToClose));
        if ((dragEdgeChoices & DRAG_LEFT) == DRAG_LEFT) {
            mDragEdges.put(DragEdge.Left, null);
        }
        if ((dragEdgeChoices & DRAG_TOP) == DRAG_TOP) {
            mDragEdges.put(DragEdge.Top, null);
        }
        if ((dragEdgeChoices & DRAG_RIGHT) == DRAG_RIGHT) {
            mDragEdges.put(DragEdge.Right, null);
        }
        if ((dragEdgeChoices & DRAG_BOTTOM) == DRAG_BOTTOM) {
            mDragEdges.put(DragEdge.Bottom, null);
        }
        int ordinal = a.getInt(R.styleable.SwipeLayout_show_mode, ShowMode.PullOut.ordinal());
        mShowMode = ShowMode.values()[ordinal];
        a.recycle();
    }
    public interface SwipeListener {
        public void onStartOpen(SwipeLayout layout);
        public void onOpen(SwipeLayout layout);
        public void onStartClose(SwipeLayout layout);
        public void onClose(SwipeLayout layout);
        public void onUpdate(SwipeLayout layout, int leftOffset, int topOffset);
        public void onHandRelease(SwipeLayout layout, float xvel, float yvel);
    }
    public void addSwipeListener(SwipeListener l) {
        mSwipeListeners.clear();
        mSwipeListeners.add(l);
    }
    public void removeSwipeListener(SwipeListener l) {
        mSwipeListeners.remove(l);
    }
    public static interface SwipeDenier {
        /*
         * Called in onInterceptTouchEvent Determines if this swipe event should
         * be denied Implement this interface if you are using views with swipe
         * gestures As a child of SwipeLayout
         *
         * @return true deny false allow
         */
        public boolean shouldDenySwipe(MotionEvent ev);
    }
    public void addSwipeDenier(SwipeDenier denier) {
        mSwipeDeniers.add(denier);
    }
    public void removeSwipeDenier(SwipeDenier denier) {
        mSwipeDeniers.remove(denier);
    }
    public void removeAllSwipeDeniers() {
        mSwipeDeniers.clear();
    }
    public interface OnRevealListener {
        public void onReveal(View child, DragEdge edge, float fraction, int distance);
    }
    /**
     * bind a view with a specific
     *
     * @param childId the view id.
     * @param l       the target
     */
    public void addRevealListener(int childId, OnRevealListener l) {
        View child = findViewById(childId);
        if (child == null) {
            throw new IllegalArgumentException("Child does not belong to SwipeListener.");
        }
        if (!mShowEntirely.containsKey(child)) {
            mShowEntirely.put(child, false);
        }
        if (mRevealListeners.get(child) == null)
            mRevealListeners.put(child, new ArrayList<OnRevealListener>());
        mRevealListeners.get(child).add(l);
    }
    /**
     * bind multiple views with an
     *
     * @param childIds the view id.
     * @param l
     */
    public void addRevealListener(int[] childIds, OnRevealListener l) {
        for (int i : childIds)
            addRevealListener(i, l);
    }
    public void removeRevealListener(int childId, OnRevealListener l) {
        View child = findViewById(childId);
        if (child == null) return;
        mShowEntirely.remove(child);
        if (mRevealListeners.containsKey(child)) mRevealListeners.get(child).remove(l);
    }
    public void removeAllRevealListeners(int childId) {
        View child = findViewById(childId);
        if (child != null) {
            mRevealListeners.remove(child);
            mShowEntirely.remove(child);
        }
    }
    private ViewDragHelper.Callback mDragHelperCallback = new ViewDragHelper.Callback() {
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            if (child == getSurfaceView()) {
                switch (mCurrentDragEdge) {
                    case Top:
                    case Bottom:
                        return getPaddingLeft();
                    case Left:
                        if (left < getPaddingLeft()) return getPaddingLeft();
                        if (left > getPaddingLeft() + mDragDistance)
                            return getPaddingLeft() + mDragDistance;
                        break;
                    case Right:
                        if (left > getPaddingLeft()) return getPaddingLeft();
                        if (left < getPaddingLeft() - mDragDistance)
                            return getPaddingLeft() - mDragDistance;
                        break;
                }
            } else if (getCurrentBottomView() == child) {
                switch (mCurrentDragEdge) {
                    case Top:
                    case Bottom:
                        return getPaddingLeft();
                    case Left:
                        if (mShowMode == ShowMode.PullOut) {
                            if (left > getPaddingLeft()) return getPaddingLeft();
                        }
                        break;
                    case Right:
                        if (mShowMode == ShowMode.PullOut) {
                            if (left < getMeasuredWidth() - mDragDistance) {
                                return getMeasuredWidth() - mDragDistance;
                            }
                        }
                        break;
                }
            }
            return left;
        }
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            if (child == getSurfaceView()) {
                switch (mCurrentDragEdge) {
                    case Left:
                    case Right:
                        return getPaddingTop();
                    case Top:
                        if (top < getPaddingTop()) return getPaddingTop();
                        if (top > getPaddingTop() + mDragDistance)
                            return getPaddingTop() + mDragDistance;
                        break;
                    case Bottom:
                        if (top < getPaddingTop() - mDragDistance) {
                            return getPaddingTop() - mDragDistance;
                        }
                        if (top > getPaddingTop()) {
                            return getPaddingTop();
                        }
                }
            } else {
                View surfaceView = getSurfaceView();
                int surfaceViewTop = surfaceView == null ? 0 : surfaceView.getTop();
                switch (mCurrentDragEdge) {
                    case Left:
                    case Right:
                        return getPaddingTop();
                    case Top:
                        if (mShowMode == ShowMode.PullOut) {
                            if (top > getPaddingTop()) return getPaddingTop();
                        } else {
                            if (surfaceViewTop + dy < getPaddingTop())
                                return getPaddingTop();
                            if (surfaceViewTop + dy > getPaddingTop() + mDragDistance)
                                return getPaddingTop() + mDragDistance;
                        }
                        break;
                    case Bottom:
                        if (mShowMode == ShowMode.PullOut) {
                            if (top < getMeasuredHeight() - mDragDistance)
                                return getMeasuredHeight() - mDragDistance;
                        } else {
                            if (surfaceViewTop + dy >= getPaddingTop())
                                return getPaddingTop();
                            if (surfaceViewTop + dy <= getPaddingTop() - mDragDistance)
                                return getPaddingTop() - mDragDistance;
                        }
                }
            }
            return top;
        }
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            boolean result = child == getSurfaceView() || getBottomViews().contains(child);
            if (result) {
                isCloseBeforeDrag = getOpenStatus() == Status.Close;
            }
            return result;
        }
        @Override
        public int getViewHorizontalDragRange(View child) {
            return mDragDistance;
        }
        @Override
        public int getViewVerticalDragRange(View child) {
            return mDragDistance;
        }
        boolean isCloseBeforeDrag = true;
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            for (SwipeListener l : mSwipeListeners) {
                l.onHandRelease(SwipeLayout.this, xvel, yvel);
            }
            processHandRelease(xvel, yvel, isCloseBeforeDrag);
            invalidate();
        }
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            View surfaceView = getSurfaceView();
            if (surfaceView == null) return;
            View currentBottomView = getCurrentBottomView();
            int evLeft = surfaceView.getLeft(),
                    evRight = surfaceView.getRight(),
                    evTop = surfaceView.getTop(),
                    evBottom = surfaceView.getBottom();
            if (changedView == surfaceView) {
                if (mShowMode == ShowMode.PullOut && currentBottomView != null) {
                    if (mCurrentDragEdge == DragEdge.Left || mCurrentDragEdge == DragEdge.Right) {
                        currentBottomView.offsetLeftAndRight(dx);
                    } else {
                        currentBottomView.offsetTopAndBottom(dy);
                    }
                }
            } else if (getBottomViews().contains(changedView)) {
                if (mShowMode == ShowMode.PullOut) {
                    surfaceView.offsetLeftAndRight(dx);
                    surfaceView.offsetTopAndBottom(dy);
                } else {
                    Rect rect = computeBottomLayDown(mCurrentDragEdge);
                    if (currentBottomView != null) {
                        currentBottomView.layout(rect.left, rect.top, rect.right, rect.bottom);
                    }
                    int newLeft = surfaceView.getLeft() + dx, newTop = surfaceView.getTop() + dy;
                    if (mCurrentDragEdge == DragEdge.Left && newLeft < getPaddingLeft())
                        newLeft = getPaddingLeft();
                    else if (mCurrentDragEdge == DragEdge.Right && newLeft > getPaddingLeft())
                        newLeft = getPaddingLeft();
                    else if (mCurrentDragEdge == DragEdge.Top && newTop < getPaddingTop())
                        newTop = getPaddingTop();
                    else if (mCurrentDragEdge == DragEdge.Bottom && newTop > getPaddingTop())
                        newTop = getPaddingTop();
                    surfaceView.layout(newLeft, newTop, newLeft + getMeasuredWidth(), newTop + getMeasuredHeight());
                }
            }
            dispatchRevealEvent(evLeft, evTop, evRight, evBottom);
            dispatchSwipeEvent(evLeft, evTop, dx, dy);
            invalidate();
        }
    };
    /**
     * the dispatchRevealEvent method may not always get accurate position, it
     * makes the view may not always get the event when the view is totally
     * show( fraction = 1), so , we need to calculate every time.
     */
    protected boolean isViewTotallyFirstShowed(View child, Rect relativePosition, DragEdge edge, int surfaceLeft,
                                               int surfaceTop, int surfaceRight, int surfaceBottom) {
        if (mShowEntirely.get(child)) return false;
        int childLeft = relativePosition.left;
        int childRight = relativePosition.right;
        int childTop = relativePosition.top;
        int childBottom = relativePosition.bottom;
        boolean r = false;
        if (getShowMode() == ShowMode.LayDown) {
            if ((edge == DragEdge.Right && surfaceRight <= childLeft)
                    || (edge == DragEdge.Left && surfaceLeft >= childRight)
                    || (edge == DragEdge.Top && surfaceTop >= childBottom)
                    || (edge == DragEdge.Bottom && surfaceBottom <= childTop)) r = true;
        } else if (getShowMode() == ShowMode.PullOut) {
            if ((edge == DragEdge.Right && childRight <= getWidth())
                    || (edge == DragEdge.Left && childLeft >= getPaddingLeft())
                    || (edge == DragEdge.Top && childTop >= getPaddingTop())
                    || (edge == DragEdge.Bottom && childBottom <= getHeight())) r = true;
        }
        return r;
    }
    protected boolean isViewShowing(View child, Rect relativePosition, DragEdge availableEdge, int surfaceLeft,
                                    int surfaceTop, int surfaceRight, int surfaceBottom) {
        int childLeft = relativePosition.left;
        int childRight = relativePosition.right;
        int childTop = relativePosition.top;
        int childBottom = relativePosition.bottom;
        if (getShowMode() == ShowMode.LayDown) {
            switch (availableEdge) {
                case Right:
                    if (surfaceRight > childLeft && surfaceRight <= childRight) {
                        return true;
                    }
                    break;
                case Left:
                    if (surfaceLeft < childRight && surfaceLeft >= childLeft) {
                        return true;
                    }
                    break;
                case Top:
                    if (surfaceTop >= childTop && surfaceTop < childBottom) {
                        return true;
                    }
                    break;
                case Bottom:
                    if (surfaceBottom > childTop && surfaceBottom <= childBottom) {
                        return true;
                    }
                    break;
            }
        } else if (getShowMode() == ShowMode.PullOut) {
            switch (availableEdge) {
                case Right:
                    if (childLeft <= getWidth() && childRight > getWidth()) return true;
                    break;
                case Left:
                    if (childRight >= getPaddingLeft() && childLeft < getPaddingLeft()) return true;
                    break;
                case Top:
                    if (childTop < getPaddingTop() && childBottom >= getPaddingTop()) return true;
                    break;
                case Bottom:
                    if (childTop < getHeight() && childTop >= getPaddingTop()) return true;
                    break;
            }
        }
        return false;
    }
    protected Rect getRelativePosition(View child) {
        View t = child;
        Rect r = new Rect(t.getLeft(), t.getTop(), 0, 0);
        while (t.getParent() != null && t != getRootView()) {
            t = (View) t.getParent();
            if (t == this) break;
            r.left += t.getLeft();
            r.top += t.getTop();
        }
        r.right = r.left + child.getMeasuredWidth();
        r.bottom = r.top + child.getMeasuredHeight();
        return r;
    }
    private int mEventCounter = 0;
    protected void dispatchSwipeEvent(int surfaceLeft, int surfaceTop, int dx, int dy) {
        DragEdge edge = getDragEdge();
        boolean open = true;
        if (edge == DragEdge.Left) {
            if (dx < 0) open = false;
        } else if (edge == DragEdge.Right) {
            if (dx > 0) open = false;
        } else if (edge == DragEdge.Top) {
            if (dy < 0) open = false;
        } else if (edge == DragEdge.Bottom) {
            if (dy > 0) open = false;
        }
        dispatchSwipeEvent(surfaceLeft, surfaceTop, open);
    }
    protected void dispatchSwipeEvent(int surfaceLeft, int surfaceTop, boolean open) {
        safeBottomView();
        Status status = getOpenStatus();
        if (!mSwipeListeners.isEmpty()) {
            mEventCounter++;
            for (SwipeListener l : mSwipeListeners) {
                if (mEventCounter == 1) {
                    if (open) {
                        l.onStartOpen(this);
                    } else {
                        l.onStartClose(this);
                    }
                }
                l.onUpdate(SwipeLayout.this, surfaceLeft - getPaddingLeft(), surfaceTop - getPaddingTop());
            }
            if (status == Status.Close) {
                for (SwipeListener l : mSwipeListeners) {
                    l.onClose(SwipeLayout.this);
                }
                mEventCounter = 0;
            }
            if (status == Status.Open) {
                View currentBottomView = getCurrentBottomView();
                if (currentBottomView != null) {
                    currentBottomView.setEnabled(true);
                }
                for (SwipeListener l : mSwipeListeners) {
                    l.onOpen(SwipeLayout.this);
                }
                mEventCounter = 0;
            }
        }
    }
    /**
     * prevent bottom view get any touch event. Especially in LayDown mode.
     */
    private void safeBottomView() {
        Status status = getOpenStatus();
        List<View> bottoms = getBottomViews();
        if (status == Status.Close) {
            for (View bottom : bottoms) {
                if (bottom != null && bottom.getVisibility() != INVISIBLE) {
                    bottom.setVisibility(INVISIBLE);
                }
            }
        } else {
            View currentBottomView = getCurrentBottomView();
            if (currentBottomView != null && currentBottomView.getVisibility() != VISIBLE) {
                currentBottomView.setVisibility(VISIBLE);
            }
        }
    }
    protected void dispatchRevealEvent(final int surfaceLeft, final int surfaceTop, final int surfaceRight,
                                       final int surfaceBottom) {
        if (mRevealListeners.isEmpty()) return;
        for (Map.Entry<View, ArrayList<OnRevealListener>> entry : mRevealListeners.entrySet()) {
            View child = entry.getKey();
            Rect rect = getRelativePosition(child);
            if (isViewShowing(child, rect, mCurrentDragEdge, surfaceLeft, surfaceTop,
                    surfaceRight, surfaceBottom)) {
                mShowEntirely.put(child, false);
                int distance = 0;
                float fraction = 0f;
                if (getShowMode() == ShowMode.LayDown) {
                    switch (mCurrentDragEdge) {
                        case Left:
                            distance = rect.left - surfaceLeft;
                            fraction = distance / (float) child.getWidth();
                            break;
                        case Right:
                            distance = rect.right - surfaceRight;
                            fraction = distance / (float) child.getWidth();
                            break;
                        case Top:
                            distance = rect.top - surfaceTop;
                            fraction = distance / (float) child.getHeight();
                            break;
                        case Bottom:
                            distance = rect.bottom - surfaceBottom;
                            fraction = distance / (float) child.getHeight();
                            break;
                    }
                } else if (getShowMode() == ShowMode.PullOut) {
                    switch (mCurrentDragEdge) {
                        case Left:
                            distance = rect.right - getPaddingLeft();
                            fraction = distance / (float) child.getWidth();
                            break;
                        case Right:
                            distance = rect.left - getWidth();
                            fraction = distance / (float) child.getWidth();
                            break;
                        case Top:
                            distance = rect.bottom - getPaddingTop();
                            fraction = distance / (float) child.getHeight();
                            break;
                        case Bottom:
                            distance = rect.top - getHeight();
                            fraction = distance / (float) child.getHeight();
                            break;
                    }
                }
                for (OnRevealListener l : entry.getValue()) {
                    l.onReveal(child, mCurrentDragEdge, Math.abs(fraction), distance);
                    if (Math.abs(fraction) == 1) {
                        mShowEntirely.put(child, true);
                    }
                }
            }
            if (isViewTotallyFirstShowed(child, rect, mCurrentDragEdge, surfaceLeft, surfaceTop,
                    surfaceRight, surfaceBottom)) {
                mShowEntirely.put(child, true);
                for (OnRevealListener l : entry.getValue()) {
                    if (mCurrentDragEdge == DragEdge.Left
                            || mCurrentDragEdge == DragEdge.Right)
                        l.onReveal(child, mCurrentDragEdge, 1, child.getWidth());
                    else
                        l.onReveal(child, mCurrentDragEdge, 1, child.getHeight());
                }
            }
        }
    }
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }
    /**
     * {@link OnLayoutChangeListener} added in API 11. I need
     * to support it from API 8.
     */
    public interface OnLayout {
        public void onLayout(SwipeLayout v);
    }
    private List<OnLayout> mOnLayoutListeners;
    public void addOnLayoutListener(OnLayout l) {
        if (mOnLayoutListeners == null) mOnLayoutListeners = new ArrayList<OnLayout>();
        mOnLayoutListeners.add(l);
    }
    public void removeOnLayoutListener(OnLayout l) {
        if (mOnLayoutListeners != null) mOnLayoutListeners.remove(l);
    }
    public void addDrag(DragEdge dragEdge, View child) {
        addDrag(dragEdge, child, null);
    }
    public void addDrag(DragEdge dragEdge, View child, ViewGroup.LayoutParams params) {
        if (params == null) {
            params = generateDefaultLayoutParams();
        }
        if (!checkLayoutParams(params)) {
            params = generateLayoutParams(params);
        }
        int gravity = -1;
        switch (dragEdge) {
            case Left:
                gravity = Gravity.LEFT;
                break;
            case Right:
                gravity = Gravity.RIGHT;
                break;
            case Top:
                gravity = Gravity.TOP;
                break;
            case Bottom:
                gravity = Gravity.BOTTOM;
                break;
        }
        if (params instanceof LayoutParams) {
            ((LayoutParams) params).gravity = gravity;
        }
        addView(child, 0, params);
    }
    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        int gravity = Gravity.NO_GRAVITY;
        try {
            gravity = (Integer) params.getClass().getField("gravity").get(params);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (gravity > 0) {
            gravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this));
            if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
                mDragEdges.put(DragEdge.Left, child);
            }
            if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
                mDragEdges.put(DragEdge.Right, child);
            }
            if ((gravity & Gravity.TOP) == Gravity.TOP) {
                mDragEdges.put(DragEdge.Top, child);
            }
            if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
                mDragEdges.put(DragEdge.Bottom, child);
            }
        } else {
            for (Map.Entry<DragEdge, View> entry : mDragEdges.entrySet()) {
                if (entry.getValue() == null) {
                    //means used the drag_edge attr, the no gravity child should be use set
                    mDragEdges.put(entry.getKey(), child);
                    break;
                }
            }
        }
        if (child == null || child.getParent() == this) {
            return;
        }
        super.addView(child, index, params);
    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        updateBottomViews();
        if (mOnLayoutListeners != null) for (int i = 0; i < mOnLayoutListeners.size(); i++) {
            mOnLayoutListeners.get(i).onLayout(this);
        }
    }
    void layoutPullOut() {
        Rect rect = computeSurfaceLayoutArea(false);
        View surfaceView = getSurfaceView();
        if (surfaceView != null) {
            surfaceView.layout(rect.left, rect.top, rect.right, rect.bottom);
            bringChildToFront(surfaceView);
        }
        rect = computeBottomLayoutAreaViaSurface(ShowMode.PullOut, rect);
        View currentBottomView = getCurrentBottomView();
        if (currentBottomView != null) {
            currentBottomView.layout(rect.left, rect.top, rect.right, rect.bottom);
        }
    }
    void layoutLayDown() {
        Rect rect = computeSurfaceLayoutArea(false);
        View surfaceView = getSurfaceView();
        if (surfaceView != null) {
            surfaceView.layout(rect.left, rect.top, rect.right, rect.bottom);
            bringChildToFront(surfaceView);
        }
        rect = computeBottomLayoutAreaViaSurface(ShowMode.LayDown, rect);
        View currentBottomView = getCurrentBottomView();
        if (currentBottomView != null) {
            currentBottomView.layout(rect.left, rect.top, rect.right, rect.bottom);
        }
    }
    private boolean mIsBeingDragged;
    private void checkCanDrag(MotionEvent ev) {
        if (mIsBeingDragged) return;
        if (getOpenStatus() == Status.Middle) {
            mIsBeingDragged = true;
            return;
        }
        Status status = getOpenStatus();
        float distanceX = ev.getRawX() - sX;
        float distanceY = ev.getRawY() - sY;
        float angle = Math.abs(distanceY / distanceX);
        angle = (float) Math.toDegrees(Math.atan(angle));
        if (getOpenStatus() == Status.Close) {
            DragEdge dragEdge;
            if (angle < 45) {
                if (distanceX > 0 && isLeftSwipeEnabled()) {
                    dragEdge = DragEdge.Left;
                } else if (distanceX < 0 && isRightSwipeEnabled()) {
                    dragEdge = DragEdge.Right;
                } else return;
            } else {
                if (distanceY > 0 && isTopSwipeEnabled()) {
                    dragEdge = DragEdge.Top;
                } else if (distanceY < 0 && isBottomSwipeEnabled()) {
                    dragEdge = DragEdge.Bottom;
                } else return;
            }
            setCurrentDragEdge(dragEdge);
        }
        boolean doNothing = false;
        if (mCurrentDragEdge == DragEdge.Right) {
            boolean suitable = (status == Status.Open && distanceX > mTouchSlop)
                    || (status == Status.Close && distanceX < -mTouchSlop);
            suitable = suitable || (status == Status.Middle);
            if (angle > 30 || !suitable) {
                doNothing = true;
            }
        }
        if (mCurrentDragEdge == DragEdge.Left) {
            boolean suitable = (status == Status.Open && distanceX < -mTouchSlop)
                    || (status == Status.Close && distanceX > mTouchSlop);
            suitable = suitable || status == Status.Middle;
            if (angle > 30 || !suitable) {
                doNothing = true;
            }
        }
        if (mCurrentDragEdge == DragEdge.Top) {
            boolean suitable = (status == Status.Open && distanceY < -mTouchSlop)
                    || (status == Status.Close && distanceY > mTouchSlop);
            suitable = suitable || status == Status.Middle;
            if (angle < 60 || !suitable) {
                doNothing = true;
            }
        }
        if (mCurrentDragEdge == DragEdge.Bottom) {
            boolean suitable = (status == Status.Open && distanceY > mTouchSlop)
                    || (status == Status.Close && distanceY < -mTouchSlop);
            suitable = suitable || status == Status.Middle;
            if (angle < 60 || !suitable) {
                doNothing = true;
            }
        }
        mIsBeingDragged = !doNothing;
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (!isSwipeEnabled()) {
            return false;
        }
        if (mClickToClose && getOpenStatus() == Status.Open && isTouchOnSurface(ev)) {
            return true;
        }
        for (SwipeDenier denier : mSwipeDeniers) {
            if (denier != null && denier.shouldDenySwipe(ev)) {
                return false;
            }
        }
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDragHelper.processTouchEvent(ev);
                mIsBeingDragged = false;
                sX = ev.getRawX();
                sY = ev.getRawY();
                //if the swipe is in middle state(scrolling), should intercept the touch
                if (getOpenStatus() == Status.Middle) {
                    mIsBeingDragged = true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                boolean beforeCheck = mIsBeingDragged;
                checkCanDrag(ev);
                if (mIsBeingDragged) {
                    ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                }
                if (!beforeCheck && mIsBeingDragged) {
                    //let children has one chance to catch the touch, and request the swipe not intercept
                    //useful when swipeLayout wrap a swipeLayout or other gestural layout
                    return false;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mIsBeingDragged = false;
                mDragHelper.processTouchEvent(ev);
                break;
            default://handle other action, such as ACTION_POINTER_DOWN/UP
                mDragHelper.processTouchEvent(ev);
        }
        return mIsBeingDragged;
    }
    private float sX = -1, sY = -1;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isSwipeEnabled()) return super.onTouchEvent(event);
        int action = event.getActionMasked();
        gestureDetector.onTouchEvent(event);
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mDragHelper.processTouchEvent(event);
                sX = event.getRawX();
                sY = event.getRawY();
            case MotionEvent.ACTION_MOVE: {
                //the drag state and the direction are already judged at onInterceptTouchEvent
                checkCanDrag(event);
                if (mIsBeingDragged) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    mDragHelper.processTouchEvent(event);
                }
                break;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mIsBeingDragged = false;
                mDragHelper.processTouchEvent(event);
                break;
            default://handle other action, such as ACTION_POINTER_DOWN/UP
                mDragHelper.processTouchEvent(event);
        }
        return super.onTouchEvent(event) || mIsBeingDragged || action == MotionEvent.ACTION_DOWN;
    }
    public boolean isClickToClose() {
        return mClickToClose;
    }
    public void setClickToClose(boolean mClickToClose) {
        this.mClickToClose = mClickToClose;
    }
    public void setSwipeEnabled(boolean enabled) {
        mSwipeEnabled = enabled;
    }
    public boolean isSwipeEnabled() {
        return mSwipeEnabled;
    }
    public boolean isLeftSwipeEnabled() {
        View bottomView = mDragEdges.get(DragEdge.Left);
        return bottomView != null && bottomView.getParent() == this
                && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Left.ordinal()];
    }
    public void setLeftSwipeEnabled(boolean leftSwipeEnabled) {
        this.mSwipesEnabled[DragEdge.Left.ordinal()] = leftSwipeEnabled;
    }
    public boolean isRightSwipeEnabled() {
        View bottomView = mDragEdges.get(DragEdge.Right);
        return bottomView != null && bottomView.getParent() == this
                && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Right.ordinal()];
    }
    public void setRightSwipeEnabled(boolean rightSwipeEnabled) {
        this.mSwipesEnabled[DragEdge.Right.ordinal()] = rightSwipeEnabled;
    }
    public boolean isTopSwipeEnabled() {
        View bottomView = mDragEdges.get(DragEdge.Top);
        return bottomView != null && bottomView.getParent() == this
                && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Top.ordinal()];
    }
    public void setTopSwipeEnabled(boolean topSwipeEnabled) {
        this.mSwipesEnabled[DragEdge.Top.ordinal()] = topSwipeEnabled;
    }
    public boolean isBottomSwipeEnabled() {
        View bottomView = mDragEdges.get(DragEdge.Bottom);
        return bottomView != null && bottomView.getParent() == this
                && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Bottom.ordinal()];
    }
    public void setBottomSwipeEnabled(boolean bottomSwipeEnabled) {
        this.mSwipesEnabled[DragEdge.Bottom.ordinal()] = bottomSwipeEnabled;
    }
    private boolean insideAdapterView() {
        return getAdapterView() != null;
    }
    private AdapterView getAdapterView() {
        ViewParent t = getParent();
        if (t instanceof AdapterView) {
            return (AdapterView) t;
        }
        return null;
    }
    private void performAdapterViewItemClick() {
        if (getOpenStatus() != Status.Close) return;
        ViewParent t = getParent();
        if (t instanceof AdapterView) {
            AdapterView view = (AdapterView) t;
            int p = view.getPositionForView(SwipeLayout.this);
            if (p != AdapterView.INVALID_POSITION) {
                view.performItemClick(view.getChildAt(p - view.getFirstVisiblePosition()), p, view
                        .getAdapter().getItemId(p));
            }
        }
    }
    private boolean performAdapterViewItemLongClick() {
        if (getOpenStatus() != Status.Close) return false;
        ViewParent t = getParent();
        if (t instanceof AdapterView) {
            AdapterView view = (AdapterView) t;
            int p = view.getPositionForView(SwipeLayout.this);
            if (p == AdapterView.INVALID_POSITION) return false;
            long vId = view.getItemIdAtPosition(p);
            boolean handled = false;
            try {
                Method m = AbsListView.class.getDeclaredMethod("performLongPress", View.class, int.class, long.class);
                m.setAccessible(true);
                handled = (boolean) m.invoke(view, SwipeLayout.this, p, vId);
            } catch (Exception e) {
                e.printStackTrace();
                if (view.getOnItemLongClickListener() != null) {
                    handled = view.getOnItemLongClickListener().onItemLongClick(view, SwipeLayout.this, p, vId);
                }
                if (handled) {
                    view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
                }
            }
            return handled;
        }
        return false;
    }
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (insideAdapterView()) {
            if (clickListener == null) {
                setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        performAdapterViewItemClick();
                    }
                });
            }
            if (longClickListener == null) {
                setOnLongClickListener(new OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        performAdapterViewItemLongClick();
                        return true;
                    }
                });
            }
        }
    }
    OnClickListener clickListener;
    @Override
    public void setOnClickListener(OnClickListener l) {
        super.setOnClickListener(l);
        clickListener = l;
    }
    OnLongClickListener longClickListener;
    @Override
    public void setOnLongClickListener(OnLongClickListener l) {
        super.setOnLongClickListener(l);
        longClickListener = l;
    }
    private Rect hitSurfaceRect;
    private boolean isTouchOnSurface(MotionEvent ev) {
        View surfaceView = getSurfaceView();
        if (surfaceView == null) {
            return false;
        }
        if (hitSurfaceRect == null) {
            hitSurfaceRect = new Rect();
        }
        surfaceView.getHitRect(hitSurfaceRect);
        return hitSurfaceRect.contains((int) ev.getX(), (int) ev.getY());
    }
    private GestureDetector gestureDetector = new GestureDetector(getContext(), new SwipeDetector());
    class SwipeDetector extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            if (mClickToClose && isTouchOnSurface(e)) {
                close();
            }
            return super.onSingleTapUp(e);
        }
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            if (mDoubleClickListener != null) {
                View target;
                View bottom = getCurrentBottomView();
                View surface = getSurfaceView();
                if (bottom != null && e.getX() > bottom.getLeft() && e.getX() < bottom.getRight()
                        && e.getY() > bottom.getTop() && e.getY() < bottom.getBottom()) {
                    target = bottom;
                } else {
                    target = surface;
                }
                mDoubleClickListener.onDoubleClick(SwipeLayout.this, target == surface);
            }
            return true;
        }
    }
    /**
     * set the drag distance, it will force set the bottom view's width or
     * height via this value.
     *
     * @param max max distance in dp unit
     */
    public void setDragDistance(int max) {
        if (max < 0) max = 0;
        mDragDistance = dp2px(max);
        requestLayout();
    }
    /**
     * There are 2 diffirent show mode.
     *
     * @param mode
     */
    public void setShowMode(ShowMode mode) {
        mShowMode = mode;
        requestLayout();
    }
    public DragEdge getDragEdge() {
        return mCurrentDragEdge;
    }
    public int getDragDistance() {
        return mDragDistance;
    }
    public ShowMode getShowMode() {
        return mShowMode;
    }
    /**
     * return null if there is no surface view(no children)
     */
    public View getSurfaceView() {
        if (getChildCount() == 0) return null;
        return getChildAt(getChildCount() - 1);
    }
    /**
     * return null if there is no bottom view
     */
    @Nullable
    public View getCurrentBottomView() {
        List<View> bottoms = getBottomViews();
        if (mCurrentDragEdge.ordinal() < bottoms.size()) {
            return bottoms.get(mCurrentDragEdge.ordinal());
        }
        return null;
    }
    /**
     * @return all bottomViews: left, top, right, bottom (may null if the edge is not set)
     */
    public List<View> getBottomViews() {
        ArrayList<View> bottoms = new ArrayList<View>();
        for (DragEdge dragEdge : DragEdge.values()) {
            bottoms.add(mDragEdges.get(dragEdge));
        }
        return bottoms;
    }
    public enum Status {
        Middle,
        Open,
        Close
    }
    /**
     * get the open status.
     */
    public Status getOpenStatus() {
        View surfaceView = getSurfaceView();
        if (surfaceView == null) {
            return Status.Close;
        }
        int surfaceLeft = surfaceView.getLeft();
        int surfaceTop = surfaceView.getTop();
        if (surfaceLeft == getPaddingLeft() && surfaceTop == getPaddingTop()) return Status.Close;
        if (surfaceLeft == (getPaddingLeft() - mDragDistance) || surfaceLeft == (getPaddingLeft() + mDragDistance)
                || surfaceTop == (getPaddingTop() - mDragDistance) || surfaceTop == (getPaddingTop() + mDragDistance))
            return Status.Open;
        return Status.Middle;
    }
    /**
     * Process the surface release event.
     *
     * @param xvel                 xVelocity
     * @param yvel                 yVelocity
     * @param isCloseBeforeDragged the open state before drag
     */
    protected void processHandRelease(float xvel, float yvel, boolean isCloseBeforeDragged) {
        float minVelocity = mDragHelper.getMinVelocity();
        View surfaceView = getSurfaceView();
        DragEdge currentDragEdge = mCurrentDragEdge;
        if (currentDragEdge == null || surfaceView == null) {
            return;
        }
        float willOpenPercent = (isCloseBeforeDragged ? .25f : .75f);
        if (currentDragEdge == DragEdge.Left) {
            if (xvel > minVelocity) open();
            else if (xvel < -minVelocity) close();
            else {
                float openPercent = 1f * getSurfaceView().getLeft() / mDragDistance;
                if (openPercent > willOpenPercent) open();
                else close();
            }
        } else if (currentDragEdge == DragEdge.Right) {
            if (xvel > minVelocity) close();
            else if (xvel < -minVelocity) open();
            else {
                float openPercent = 1f * (-getSurfaceView().getLeft()) / mDragDistance;
                if (openPercent > willOpenPercent) open();
                else close();
            }
        } else if (currentDragEdge == DragEdge.Top) {
            if (yvel > minVelocity) open();
            else if (yvel < -minVelocity) close();
            else {
                float openPercent = 1f * getSurfaceView().getTop() / mDragDistance;
                if (openPercent > willOpenPercent) open();
                else close();
            }
        } else if (currentDragEdge == DragEdge.Bottom) {
            if (yvel > minVelocity) close();
            else if (yvel < -minVelocity) open();
            else {
                float openPercent = 1f * (-getSurfaceView().getTop()) / mDragDistance;
                if (openPercent > willOpenPercent) open();
                else close();
            }
        }
    }
    /**
     * smoothly open surface.
     */
    public void open() {
        open(true, true);
    }
    public void open(boolean smooth) {
        open(smooth, true);
    }
    public void open(boolean smooth, boolean notify) {
        View surface = getSurfaceView(), bottom = getCurrentBottomView();
        if (surface == null) {
            return;
        }
        int dx, dy;
        Rect rect = computeSurfaceLayoutArea(true);
        if (smooth) {
            mDragHelper.smoothSlideViewTo(surface, rect.left, rect.top);
        } else {
            dx = rect.left - surface.getLeft();
            dy = rect.top - surface.getTop();
            surface.layout(rect.left, rect.top, rect.right, rect.bottom);
            if (getShowMode() == ShowMode.PullOut) {
                Rect bRect = computeBottomLayoutAreaViaSurface(ShowMode.PullOut, rect);
                if (bottom != null) {
                    bottom.layout(bRect.left, bRect.top, bRect.right, bRect.bottom);
                }
            }
            if (notify) {
                dispatchRevealEvent(rect.left, rect.top, rect.right, rect.bottom);
                dispatchSwipeEvent(rect.left, rect.top, dx, dy);
            } else {
                safeBottomView();
            }
        }
        invalidate();
    }
    public void open(DragEdge edge) {
        setCurrentDragEdge(edge);
        open(true, true);
    }
    public void open(boolean smooth, DragEdge edge) {
        setCurrentDragEdge(edge);
        open(smooth, true);
    }
    public void open(boolean smooth, boolean notify, DragEdge edge) {
        setCurrentDragEdge(edge);
        open(smooth, notify);
    }
    /**
     * smoothly close surface.
     */
    public void close() {
        close(true, true);
    }
    public void close(boolean smooth) {
        close(smooth, true);
    }
    /**
     * close surface
     *
     * @param smooth smoothly or not.
     * @param notify if notify all the listeners.
     */
    public void close(boolean smooth, boolean notify) {
        View surface = getSurfaceView();
        if (surface == null) {
            return;
        }
        int dx, dy;
        if (smooth)
            mDragHelper.smoothSlideViewTo(getSurfaceView(), getPaddingLeft(), getPaddingTop());
        else {
            Rect rect = computeSurfaceLayoutArea(false);
            dx = rect.left - surface.getLeft();
            dy = rect.top - surface.getTop();
            surface.layout(rect.left, rect.top, rect.right, rect.bottom);
            if (notify) {
                dispatchRevealEvent(rect.left, rect.top, rect.right, rect.bottom);
                dispatchSwipeEvent(rect.left, rect.top, dx, dy);
            } else {
                safeBottomView();
            }
        }
        invalidate();
    }
    public void toggle() {
        toggle(true);
    }
    public void toggle(boolean smooth) {
        if (getOpenStatus() == Status.Open)
            close(smooth);
        else if (getOpenStatus() == Status.Close) open(smooth);
    }
    /**
     * a helper function to compute the Rect area that surface will hold in.
     *
     * @param open open status or close status.
     */
    private Rect computeSurfaceLayoutArea(boolean open) {
        int l = getPaddingLeft(), t = getPaddingTop();
        if (open) {
            if (mCurrentDragEdge == DragEdge.Left)
                l = getPaddingLeft() + mDragDistance;
            else if (mCurrentDragEdge == DragEdge.Right)
                l = getPaddingLeft() - mDragDistance;
            else if (mCurrentDragEdge == DragEdge.Top)
                t = getPaddingTop() + mDragDistance;
            else t = getPaddingTop() - mDragDistance;
        }
        return new Rect(l, t, l + getMeasuredWidth(), t + getMeasuredHeight());
    }
    private Rect computeBottomLayoutAreaViaSurface(ShowMode mode, Rect surfaceArea) {
        Rect rect = surfaceArea;
        View bottomView = getCurrentBottomView();
        int bl = rect.left, bt = rect.top, br = rect.right, bb = rect.bottom;
        if (mode == ShowMode.PullOut) {
            if (mCurrentDragEdge == DragEdge.Left)
                bl = rect.left - mDragDistance;
            else if (mCurrentDragEdge == DragEdge.Right)
                bl = rect.right;
            else if (mCurrentDragEdge == DragEdge.Top)
                bt = rect.top - mDragDistance;
            else bt = rect.bottom;
            if (mCurrentDragEdge == DragEdge.Left || mCurrentDragEdge == DragEdge.Right) {
                bb = rect.bottom;
                br = bl + (bottomView == null ? 0 : bottomView.getMeasuredWidth());
            } else {
                bb = bt + (bottomView == null ? 0 : bottomView.getMeasuredHeight());
                br = rect.right;
            }
        } else if (mode == ShowMode.LayDown) {
            if (mCurrentDragEdge == DragEdge.Left)
                br = bl + mDragDistance;
            else if (mCurrentDragEdge == DragEdge.Right)
                bl = br - mDragDistance;
            else if (mCurrentDragEdge == DragEdge.Top)
                bb = bt + mDragDistance;
            else bt = bb - mDragDistance;
        }
        return new Rect(bl, bt, br, bb);
    }
    private Rect computeBottomLayDown(DragEdge dragEdge) {
        int bl = getPaddingLeft(), bt = getPaddingTop();
        int br, bb;
        if (dragEdge == DragEdge.Right) {
            bl = getMeasuredWidth() - mDragDistance;
        } else if (dragEdge == DragEdge.Bottom) {
            bt = getMeasuredHeight() - mDragDistance;
        }
        if (dragEdge == DragEdge.Left || dragEdge == DragEdge.Right) {
            br = bl + mDragDistance;
            bb = bt + getMeasuredHeight();
        } else {
            br = bl + getMeasuredWidth();
            bb = bt + mDragDistance;
        }
        return new Rect(bl, bt, br, bb);
    }
    public void setOnDoubleClickListener(DoubleClickListener doubleClickListener) {
        mDoubleClickListener = doubleClickListener;
    }
    public interface DoubleClickListener {
        public void onDoubleClick(SwipeLayout layout, boolean surface);
    }
    private int dp2px(float dp) {
        return (int) (dp * getContext().getResources().getDisplayMetrics().density + 0.5f);
    }
    /**
     * Deprecated, use {@link #addDrag(DragEdge, View)}
     */
    @Deprecated
    public void setDragEdge(DragEdge dragEdge) {
        if (getChildCount() >= 2) {
            mDragEdges.put(dragEdge, getChildAt(getChildCount() - 2));
        }
        setCurrentDragEdge(dragEdge);
    }
    public void onViewRemoved(View child) {
        for (Map.Entry<DragEdge, View> entry : new HashMap<DragEdge, View>(mDragEdges).entrySet()) {
            if (entry.getValue() == child) {
                mDragEdges.remove(entry.getKey());
            }
        }
    }
    public Map<DragEdge, View> getDragEdgeMap() {
        return mDragEdges;
    }
    /**
     * Deprecated, use {@link #getDragEdgeMap()}
     */
    @Deprecated
    public List<DragEdge> getDragEdges() {
        return new ArrayList<DragEdge>(mDragEdges.keySet());
    }
    /**
     * Deprecated, use {@link #addDrag(DragEdge, View)}
     */
    @Deprecated
    public void setDragEdges(List<DragEdge> dragEdges) {
        for (int i = 0, size = Math.min(dragEdges.size(), getChildCount() - 1); i < size; i++) {
            DragEdge dragEdge = dragEdges.get(i);
            mDragEdges.put(dragEdge, getChildAt(i));
        }
        if (dragEdges.size() == 0 || dragEdges.contains(DefaultDragEdge)) {
            setCurrentDragEdge(DefaultDragEdge);
        } else {
            setCurrentDragEdge(dragEdges.get(0));
        }
    }
    /**
     * Deprecated, use {@link #addDrag(DragEdge, View)}
     */
    @Deprecated
    public void setDragEdges(DragEdge... mDragEdges) {
        setDragEdges(Arrays.asList(mDragEdges));
    }
    /**
     * Deprecated, use {@link #addDrag(DragEdge, View)}
     * When using multiple drag edges it's a good idea to pass the ids of the views that
     * you're using for the left, right, top bottom views (-1 if you're not using a particular view)
     */
    @Deprecated
    public void setBottomViewIds(int leftId, int rightId, int topId, int bottomId) {
        addDrag(DragEdge.Left, findViewById(leftId));
        addDrag(DragEdge.Right, findViewById(rightId));
        addDrag(DragEdge.Top, findViewById(topId));
        addDrag(DragEdge.Bottom, findViewById(bottomId));
    }
    private float getCurrentOffset() {
        if (mCurrentDragEdge == null) return 0;
        return mEdgeSwipesOffset[mCurrentDragEdge.ordinal()];
    }
    private void setCurrentDragEdge(DragEdge dragEdge) {
        if (mCurrentDragEdge != dragEdge) {
            mCurrentDragEdge = dragEdge;
            updateBottomViews();
        }
    }
    private void updateBottomViews() {
        View currentBottomView = getCurrentBottomView();
        if (currentBottomView != null) {
            if (mCurrentDragEdge == DragEdge.Left || mCurrentDragEdge == DragEdge.Right) {
                mDragDistance = currentBottomView.getMeasuredWidth() - dp2px(getCurrentOffset());
            } else
                mDragDistance = currentBottomView.getMeasuredHeight() - dp2px(getCurrentOffset());
        }
        if (mShowMode == ShowMode.PullOut)
            layoutPullOut();
        else if (mShowMode == ShowMode.LayDown) layoutLayDown();
        safeBottomView();
    }
}
app/src/main/res/drawable/del_house.png
app/src/main/res/drawable/editor_house.png
app/src/main/res/drawable/move_house.png
app/src/main/res/layout/frgment_house_list_line.xml
@@ -1,79 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.hdl.photovoltaic.widget.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_parent_swipeLayout"
    android:layout_width="match_parent"
    android:layout_height="142dp"
    android:background="@color/text_F9FAFB">
    android:layout_height="wrap_content">
    <include
        android:id="@+id/swipe_layout_il"
        layout="@layout/swipe_right_layout" />
    <RelativeLayout
        android:id="@+id/fragment_house_list_line_parent_rl"
        android:layout_width="0dp"
        android:layout_height="130dp"
        android:layout_alignParentStart="true"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="@dimen/dp_12"
        android:layout_marginStart="@dimen/dp_20"
        android:layout_marginEnd="@dimen/dp_20"
        android:background="@drawable/house_list_line_parent_bg">
        android:id="@+id/item_parent_rl"
        android:layout_width="match_parent"
        android:layout_height="142dp"
        android:background="@color/text_F9FAFB">
        <ImageView
            android:id="@+id/fragment_house_list_line_left_iv"
            android:layout_width="113dp"
        <RelativeLayout
            android:id="@+id/fragment_house_list_line_parent_rl"
            android:layout_width="0dp"
            android:layout_height="130dp"
            android:scaleType="centerCrop"
            />
        <TextView
            android:id="@+id/fragment_house_list_line_name_tv"
            android:layout_width="170dp"
            android:layout_height="@dimen/dp_23"
            android:layout_marginStart="@dimen/dp_15"
            android:layout_marginTop="@dimen/dp_28"
            android:layout_toEndOf="@+id/fragment_house_list_line_left_iv"
            android:gravity="center_vertical|start"
            android:text="@string/power_station"
            android:textColor="@color/text_90000000"
            android:ellipsize="end"
            android:singleLine="true"
            android:textSize="@dimen/text_16" />
        <TextView
            android:id="@+id/fragment_house_list_line_capacity_tv"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/dp_17"
            android:layout_below="@+id/fragment_house_list_line_name_tv"
            android:layout_alignStart="@+id/fragment_house_list_line_name_tv"
            android:layout_marginTop="@dimen/dp_13"
            android:gravity="center_vertical"
            android:text="@string/my_power_station_installed_capacity"
            android:textColor="@color/text_40000000"
            android:textSize="@dimen/text_12" />
        <TextView
            android:id="@+id/fragment_house_list_line_power_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/fragment_house_list_line_capacity_tv"
            android:layout_alignStart="@+id/fragment_house_list_line_capacity_tv"
            android:layout_marginTop="@dimen/dp_13"
            android:gravity="center_vertical"
            android:text="@string/power_station_generated_power"
            android:textColor="@color/text_40000000"
            android:textSize="@dimen/text_12" />
        <TextView
            android:id="@+id/fragment_house_list_line_state_tv"
            android:layout_width="@dimen/dp_48"
            android:layout_height="@dimen/dp_33"
            android:layout_alignParentTop="true"
            android:layout_alignParentStart="true"
            android:layout_alignParentEnd="true"
            android:background="@drawable/state_ffb300"
            android:gravity="center"
            android:text="@string/my_power_station_connecting"
            android:textColor="@color/text_FFFFFFFF"
            android:textSize="@dimen/text_12" />
            android:layout_marginStart="@dimen/dp_20"
            android:layout_marginTop="@dimen/dp_12"
            android:layout_marginEnd="@dimen/dp_20"
            android:background="@drawable/house_list_line_parent_bg">
            <ImageView
                android:id="@+id/fragment_house_list_line_left_iv"
                android:layout_width="113dp"
                android:layout_height="130dp"
                android:scaleType="centerCrop" />
            <TextView
                android:id="@+id/fragment_house_list_line_name_tv"
                android:layout_width="170dp"
                android:layout_height="@dimen/dp_23"
                android:layout_marginStart="@dimen/dp_15"
                android:layout_marginTop="@dimen/dp_28"
                android:layout_toEndOf="@+id/fragment_house_list_line_left_iv"
                android:ellipsize="end"
                android:gravity="center_vertical|start"
                android:singleLine="true"
                android:text="@string/power_station"
                android:textColor="@color/text_90000000"
                android:textSize="@dimen/text_16" />
            <TextView
                android:id="@+id/fragment_house_list_line_capacity_tv"
                android:layout_width="wrap_content"
                android:layout_height="@dimen/dp_17"
                android:layout_below="@+id/fragment_house_list_line_name_tv"
                android:layout_alignStart="@+id/fragment_house_list_line_name_tv"
                android:layout_marginTop="@dimen/dp_13"
                android:gravity="center_vertical"
                android:text="@string/my_power_station_installed_capacity"
                android:textColor="@color/text_40000000"
                android:textSize="@dimen/text_12" />
            <TextView
                android:id="@+id/fragment_house_list_line_power_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/fragment_house_list_line_capacity_tv"
                android:layout_alignStart="@+id/fragment_house_list_line_capacity_tv"
                android:layout_marginTop="@dimen/dp_13"
                android:gravity="center_vertical"
                android:text="@string/power_station_generated_power"
                android:textColor="@color/text_40000000"
                android:textSize="@dimen/text_12" />
            <TextView
                android:id="@+id/fragment_house_list_line_state_tv"
                android:layout_width="@dimen/dp_48"
                android:layout_height="@dimen/dp_33"
                android:layout_alignParentTop="true"
                android:layout_alignParentEnd="true"
                android:background="@drawable/state_ffb300"
                android:gravity="center"
                android:text="@string/my_power_station_connecting"
                android:textColor="@color/text_FFFFFFFF"
                android:textSize="@dimen/text_12" />
        </RelativeLayout>
    </RelativeLayout>
</com.hdl.photovoltaic.widget.SwipeLayout>
</RelativeLayout>
app/src/main/res/layout/swipe_right_layout.xml
New file
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="82dp"
    android:layout_height="142dp"
    android:layout_marginTop="@dimen/dp_12"
    android:orientation="vertical">
    <View
        android:layout_width="wrap_content"
        android:layout_height="@dimen/dp_19" />
    <ImageView
        android:id="@+id/move_home_iv"
        android:layout_width="@dimen/dp_50"
        android:layout_height="@dimen/dp_50"
        android:layout_marginStart="@dimen/dp_16"
        android:src="@drawable/move_house" />
    <View
        android:layout_width="wrap_content"
        android:layout_height="@dimen/dp_16" />
    <ImageView
        android:id="@+id/del_home_iv"
        android:layout_width="@dimen/dp_50"
        android:layout_height="@dimen/dp_50"
        android:layout_marginStart="@dimen/dp_16"
        android:src="@drawable/del_house" />
</LinearLayout>
app/src/main/res/layout/toolbar_top_view_52.xml
@@ -48,6 +48,14 @@
        android:orientation="horizontal">
        <ImageView
            android:id="@+id/top_edit_iv"
            android:layout_width="@dimen/dp_28"
            android:layout_height="@dimen/dp_28"
            android:adjustViewBounds="true"
            android:scaleType="centerInside"
            android:visibility="gone" />
        <ImageView
            android:id="@+id/top_more_iv"
            android:layout_width="@dimen/dp_28"
            android:layout_height="@dimen/dp_28"
app/src/main/res/values/styles.xml
@@ -4,4 +4,6 @@
        <item name="android:background">@color/black_overlay</item>
        <item name="android:buttonBarStyle">?android:attr/buttonBarStyle</item>
    </style>
</resources>
app/src/main/res/values/values.xml
New file
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SwipeLayout">
        <attr name="drag_edge">
            <flag name="left" value="1" />
            <flag name="right" value="2" />
            <flag name="top" value="4" />
            <flag name="bottom" value="8" />
        </attr>
        <attr name="leftEdgeSwipeOffset" format="dimension" />
        <attr name="rightEdgeSwipeOffset" format="dimension" />
        <attr name="topEdgeSwipeOffset" format="dimension" />
        <attr name="bottomEdgeSwipeOffset" format="dimension" />
        <attr name="show_mode" format="enum">
            <enum name="lay_down" value="0" />
            <enum name="pull_out" value="1" />
        </attr>
        <attr name="clickToClose" format="boolean" />
    </declare-styleable>
</resources>