前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >对于Android业务开发的一些理解总结

对于Android业务开发的一些理解总结

作者头像
萬物並作吾以觀復
发布2019-03-15 16:24:57
6020
发布2019-03-15 16:24:57
举报

关于 PopupWindow ,很多博客有谈到利用 Builder 设计模式的链式写法,以下是我项目中的类似写法

  /**
     * 显示选择性别
     */
    private void showGenderPopWindow() {
        if (null == genderPopupWindow) {
            CommonPop.Builder builder = new CommonPop.Builder(BundledTravelCardActivity.this);
            View layView = LayoutInflater.from(BundledTravelCardActivity.this).inflate(R.layout.popup_window_bundled_travel_card, null);
            genderPopupWindow = builder.setView(layView)
                    .setBackGroundLevel(0.7f).setOutsideTouchable(true)
                    .setWidthAndHeight(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
                    .setAnimationStyle(R.style.MyPopupWindow_alpha_style)
                    .create();
            TextView tvOne = layView.findViewById(R.id.tv_one);
            tvOne.setText(getString(R.string.man));
            tvOne.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    tvGender.setText(getString(R.string.man));
                    genderPopupWindow.dismiss();
                }
            });
            TextView tvTwo = layView.findViewById(R.id.tv_two);
            tvTwo.setText(getString(R.string.woman));
            tvTwo.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    tvGender.setText(getString(R.string.woman));
                    genderPopupWindow.dismiss();
                }
            });
        }
        genderPopupWindow.showAtLocation(getWindow().getDecorView(), Gravity.BOTTOM, 0, 0);
    }

但是这种写法也有所局限性,如果该提示框在其他地方也有显示,代码无法复用,这时有人会说了,把代码写到一个工具类,不就可以达到复用了吗,问题是假如我们要对 PopupWindow 的 子 view 进行点击事件监听,那么还要进行接口回调,假设一个业务场景,点击了某一个按钮,然后我们通过接口回调触发 view 层的一个方法,最后再改变 PopupWindow 里子 view 的背景图片以表示被点击,这样我们就必须把需要改变状态的 view 申明为全局变量,而且如果类似的 PopupWindow 很多,我们是把它们写到一个工具类里面还是单独写呢,毫无疑问,放在一块是不合理的容易造成 bug,而且代码混乱违背单一原则,单独写工具栏就还不如对PopupWindow 进行抽象封装,把 view 的事件和UI都放到该实现类里面,还可以避免写重复代码,提取共性,以上是我的理解,下面是抽象 PopupWindow 代码。

/**
 * 抽象的 PopupWindow 基类
 */
public abstract class BasePopupWindow extends PopupWindow {
    private static final String TAG = BasePopupWindow.class.getSimpleName();
    public Context mContext;
    public View mView;
    /**
     * 屏幕灰度级别
     */
    private float mLevel;

    public BasePopupWindow(Context context) {
        super(context);
        if (null == context) {
            LogUtils.e(TAG, "BasePopupWindow context 为空");
            return;
        }
        this.mContext = context;
        this.setBackgroundDrawable(new BitmapDrawable());
        this.setFocusable(true);
        ColorDrawable dw = new ColorDrawable(0x00000000);
        this.setBackgroundDrawable(dw);
        initAnimation();
        initSetting();
        initView();
        initListener();
        setContentView(mView);
    }

    public void setmLevel(float mLevel) {
        this.mLevel = mLevel;
    }

    /**
     * 初始化 PopupWindow 的设置
     */
    public abstract void initSetting();

    /**
     * 初始化布局
     */
    public abstract void initView();

    /**
     * 初始化监听事件
     */
    public void initListener() {
    }

    private void setBackGroundLevel(float level) {
        Window window = ((Activity) mContext).getWindow();
        WindowManager.LayoutParams params = window.getAttributes();
        params.alpha = level;
        window.setAttributes(params);
    }

    @Override
    public void dismiss() {
        super.dismiss();
        setBackGroundLevel(1.0f);
    }

    @Override
    public void showAsDropDown(View anchor) {
        this.showAsDropDown(anchor, 0, 0);
    }

    @Override
    public void showAsDropDown(View anchor, int xoff, int yoff) {
        this.showAsDropDown(anchor, xoff, yoff, Gravity.TOP | Gravity.START);
    }

    @Override
    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
        super.showAsDropDown(anchor, xoff, yoff, gravity);
        initBackGroundLevel();
    }

    /**
     * 初始化背景颜色灰度
     */
    private void initBackGroundLevel() {
        if(mLevel == 0){
            mLevel = 0.4f;
        }
        setBackGroundLevel(mLevel);
    }

    @Override
    public void showAtLocation(View parent, int gravity, int x, int y) {
        super.showAtLocation(parent, gravity, x, y);
        initBackGroundLevel();
    }

    /**
     * 设置默认的动画,需要设置其他动画直接重写并删除 super 实现即可
     */
    public void initAnimation() {
        setAnimationStyle(R.style.MyPopupWindow_alpha_style);
    }

}

第二,不知道大家对商城类 APP 的我的订单页面有没有印象,它们的布局是一样的,只是 item 的类型和点击事件不一样,那么像这样的页面我们该怎么去设计呢,或许有的朋友会说干脆写一个类,把所以的处理放一块,还有的朋友会说,每一个页面都写一个类,但是关于代码复用怎么处理呢,如何从业务中提取出共性逻辑,假如我们以后业务修改了,又如何去处理呢,以下是我在项目中的业务界面。

我的订单页面

简单的说一下该页面的布局和业务逻辑,待付款页面的 item 有关闭订单和确认支付两个按钮,而待收货页面的 item 里有确认收货按钮,已关闭的页面的 item 有删除按钮,按钮的操作逻辑顾名思义,所有页面请求订单的接口一致,按参数来区分,都有上拉刷新和下拉刷新,有 loading view 和 empty view ,根据以上,我们可以提取一下共性代码来复用。

**
 * 我的订单 Fragment 的基类
 * 提取共有逻辑
 */
public class BaseOrderFragment extends BaseLazyLoadFragment implements IOrderListView, IOrderListListenerView {

    @BindView(R.id.rc_content)
    RecyclerView rcContent;
    @BindView(R.id.state_view_integral_record)
    MultiStateView stateViewIntegralRecord;
    @BindView(R.id.swipe_relayout)
    android.support.v4.widget.SwipeRefreshLayout mRefLayout;
    // 是否为加载动画中
    private boolean isLoading = true;
    // 设置不允许上拉加载
    private boolean noLoadMore;
    /**
     * 订单状态
     */
    private String status;
    public String uId;
    private String paystatus;
    private int page = 1;
    // 获取列表数据的 P
    private OrderListPImpl orderListP;
    private OrderListAdapter mAdapter;
    // 订单点击事件的 P
    public OrderListListenerPImpl listenerP;
    // 等待收货的回调
    private OrderListAdapter.WaitReceivingCall mWaitReceivingCall;
    // 关闭订单的回调
    private OrderListAdapter.CloseOrderCall mCloseOrderCall;
    // 待支付的回调
    private OrderListAdapter.WaitPayCall mWaitPayCall;

    public static BaseOrderFragment getInstance(String status, String uId, String paystatus) {
        BaseOrderFragment fragment = new BaseOrderFragment();
        Bundle bundle = new Bundle();
        bundle.putString("status", status);
        bundle.putString("uId", uId);
        bundle.putString("paystatus", paystatus);
        fragment.setArguments(bundle);
        return fragment;
    }

    public void setmWaitReceivingCall(OrderListAdapter.WaitReceivingCall mWaitReceivingCall) {
        this.mWaitReceivingCall = mWaitReceivingCall;
    }

    public void setmCloseOrderCall(OrderListAdapter.CloseOrderCall mCloseOrderCall) {
        this.mCloseOrderCall = mCloseOrderCall;
    }

    public void setmWaitPayCall(OrderListAdapter.WaitPayCall mWaitPayCall) {
        this.mWaitPayCall = mWaitPayCall;
    }

    @Override
    public void fetchData() {
        getDatas();
    }

    public void getDatas(){
        if (null == orderListP) {
            orderListP = new OrderListPImpl();
            orderListP.attachView(this);
        }
        orderListP.prestOrderList(String.valueOf(page), uId, status, "order_list", paystatus);
    }

    @Override
    protected void initViews() {
        mRefLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                if(null != mAdapter){
                    noLoadMore = true;
                    mAdapter.setEnableLoadMore(false);
                }
                showLoading();
                mRefLayout.setRefreshing(false);
                getDatas();
            }
        });
    }

    @Override
    protected void initDatas() {
        if (null != getArguments()) {
            Bundle bundle = getArguments();
            status = bundle.getString("status");
            uId = bundle.getString("uId");
            paystatus = bundle.getString("paystatus");
        }
        if(null == listenerP){
            listenerP = new OrderListListenerPImpl();
            listenerP.attachView(this);
        }
    }

    /**
     * 初始化内容
     * @param bean
     */
    private void initContentView(OrderListBean bean) {
        if (page == 1) {
            page++;
            setDataInitiated(true);
            if (!checkData(bean)) {
                stateViewIntegralRecord.setViewState(MultiStateView.VIEW_STATE_EMPTY);
                return;
            }
            if(null == mAdapter){
                mAdapter = new OrderListAdapter(bean.getData().getData(), mWaitReceivingCall,
                        mCloseOrderCall, mWaitPayCall);
                rcContent.setLayoutManager(new LinearLayoutManager(mContext));
                rcContent.setAdapter(mAdapter);
                mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {
                    @Override
                    public void onLoadMoreRequested() {
                        getDatas();
                    }
                });
            }else {
                mAdapter.replaceData(bean.getData().getData());
            }
            mAdapter.loadMoreComplete();
            if(isLoading){
                isLoading = false;
                resetLoadMore();
                // 为了避免网速很快,加载动画造成闪烁的效果,体验不佳
                stateViewIntegralRecord.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if(null != stateViewIntegralRecord){
                            stateViewIntegralRecord.setViewState(MultiStateView.VIEW_STATE_CONTENT);
                        }
                    }
                }, 500);
            }
        } else {
            if (checkData(bean)) {
                page++;
                mAdapter.addData(bean.getData().getData());
                mAdapter.loadMoreComplete();
            } else {
                mAdapter.loadMoreEnd();
            }
        }
    }

    /**
     * 重置可上拉加载
     */
    private void resetLoadMore() {
        if(noLoadMore && null != mAdapter){
            noLoadMore = false;
            mAdapter.setEnableLoadMore(true);
        }
    }

    /**
     * 检查数据的合法性
     * @param bean
     * @return
     */
    private boolean checkData(OrderListBean bean) {
        try {
            if (null != bean &&
                    null != bean.getData().getData() &&
                    bean.getData().getData().size() > 0) {
                return true;
            }
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    protected int getContentLayoutRes() {
        return R.layout.layout_rc;
    }

    @Override
    public void showNetError() {
        if(!isLoading && null != mAdapter){
            mAdapter.loadMoreFail();
        }else {
            resetLoadMore();
            showEmptyView();
        }
    }

    // 显示空的 View
    private void showEmptyView() {
        isLoading = false;
        stateViewIntegralRecord.postDelayed(new Runnable() {
            @Override
            public void run() {
                if(null != stateViewIntegralRecord){
                    stateViewIntegralRecord.setViewState(MultiStateView.VIEW_STATE_EMPTY);
                }
            }
        }, 500);
    }

    @Override
    public void showDataError() {
        if(!isLoading && null != mAdapter){
            mAdapter.loadMoreEnd();
        }else {
            resetLoadMore();
            showEmptyView();
        }
    }

    @Override
    public void showMsg(String msg) {
        showToast(msg);
    }

    @Override
    public Activity getMyContext() {
        return mContext;
    }

    @Override
    public void updateDatas(String msg) {
        showToast(msg);
        showLoading();
        getDatas();
    }

    // 显示加载中动画
    public void showLoading() {
        page = 1;
        isLoading = true;
        setDataInitiated(false);
        stateViewIntegralRecord.setViewState(MultiStateView.VIEW_STATE_LOADING);
    }

    @Override
    public void setOrderList(OrderListBean bean) {
        initContentView(bean);
    }
}

IOrderListView 是查询订单数据页面的 view ,IOrderListListenerView 则是用户在操作点击时候后返回数据的 view,用来区别是否操作成功,以下是订单列表的 xml 布局代码

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/swipe_relayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.kennyc.view.MultiStateView
        android:id="@+id/state_view_integral_record"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:msv_animateViewChanges="true"
        app:msv_emptyView="@layout/empty_integral_record"
        app:msv_loadingView="@layout/loading_default_view"
        app:msv_viewState="loading">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rc_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </com.kennyc.view.MultiStateView>
</android.support.v4.widget.SwipeRefreshLayout>

就拿待支付页面举例,我们需要在点击确认支付的时候弹出选择支付的提示框,然后调起相应的第三方支付客户端支付

/**
 * 待付款 fragment
 */
public class WaitPaymentFragment extends BaseOrderFragment implements IPayView,
        OrderListAdapter.WaitPayCall{

    public static WaitPaymentFragment getInstance(String status, String uId, String paystatus) {
        WaitPaymentFragment fragment = new WaitPaymentFragment();
        Bundle bundle = new Bundle();
        bundle.putString("status", status);
        bundle.putString("uId", uId);
        bundle.putString("paystatus", paystatus);
        fragment.setArguments(bundle);
        return fragment;
    }

    private IWXAPI api;
    private WaitPayPImpl waitPayP;
    private OrderListPayPop orderListPayPop;

    @Override
    protected void initDatas() {
        if(null == waitPayP){
            waitPayP = new WaitPayPImpl();
            waitPayP.attachView(this);
        }
        setmWaitPayCall(this);
        api = WXAPIFactory.createWXAPI(mContext, Constants.WeChatId);
        api.registerApp(Constants.WeChatId);
        super.initDatas();
    }

    @Override
    public void isPaySuccess(final boolean tag) {
        mContext.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if(tag){
                    showToast("支付宝支付成功");
                    showLoading();
                    getDatas();
                }else {
                    showToast("支付宝支付失败");
                }
            }
        });
    }

    @Override
    public void setWeChatDatas(PayReq datas) {
        // 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信
        api.sendReq(datas);
    }

    @Override
    public void waitPay(final String amount, final String ordersn, final String subject, int position) {
        LogUtils.e("setPayParams", amount + "   " + ordersn + "   " + subject);
        if(null == orderListPayPop){
            orderListPayPop = new OrderListPayPop(mContext);
        }
        orderListPayPop.setAmount(amount);
        orderListPayPop.setOrderListPayCall(new OrderListPayPop.OrderListPayCall() {
            @Override
            public void callAliPay() {
                waitPayP.prestAliPayOrder(amount, ordersn, subject);
            }

            @Override
            public void callWeChat() {
                waitPayP.prestWeChatOrder(amount, ordersn, subject);
            }
        });
        orderListPayPop.showAtLocation(mContext.getWindow().getDecorView(),
                Gravity.CENTER, 0, 0);
    }

    @Override
    public void closeWaitPayOrder(String ordersn, int position) {
        LogUtils.e("closeWaitPayOrder", ordersn);
        listenerP.callListener("order_close", uId, ordersn);
    }
}

这里再简单说明一下,IPayView 是支付的 view 层,OrderListAdapter.WaitPayCall 是传递到 Adapter 的接口回调,用于回调点击事件。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.03.11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档