美文网首页Android开发Android开发经验谈Android技术知识
对于 Android 业务开发的一些理解总结

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

作者: Android开发架构 | 来源:发表于2019-04-09 15:11 被阅读17次

    关于 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 的接口回调,用于回调点击事件。

    免费获取安卓开发架构的资料(包括Fultter、高级UI、性能优化、架构师课程、 NDK、Kotlin、混合式开发(ReactNative+Weex)和一线互联网公司关于android面试的题目汇总可以加:936332305 / 链接:点击链接加入【安卓开发架构】

    相关文章

      网友评论

        本文标题:对于 Android 业务开发的一些理解总结

        本文链接:https://www.haomeiwen.com/subject/ytwbiqtx.html