一、前言
随着国内手游版号申请难度的增加,以及防沉迷等一系列政策的影响,很多国内开发者纷纷开始寻求海外发行之路。那么手游出海首要的是需要一套适合海外发行和运营的手游SDK联运系统。
本系列我们就来开发一套这样的SDK,我们暂且称这套SDK为UGSDK。
整个UGSDK项目,暂时可以分为三大部分——Android客户端SDK部分、iOS客户端SDK部分以及服务端部分(目前不考虑H5游戏部分)。
本篇主要介绍UGSDK项目中Android客户端部分中的UI和流程控制设计。
二、UI设计
1、设计原则
1、每个界面采用单独一个fragment,fragment能重复利用尽可能重复利用。
2、单个fragment中,只能放一个UI界面,不可以将多个UI界面放到一个fragment中。
3、fragment中禁止处理流程跳转,也就是不会直接在一个fragment中push进另一个fragment。
4、流程的控制(fragment的跳转),统一在fragment归属的activity中处理。
5、fragment和activity之间的流程交互,采用回调的方式处理。
2、设计样例:
我们来看下游客升级提示界面的fragment, 演示如何设计单个UI界面的fragment以及处理流程中需要被所属Activity托管的部分:
public class VisitorFragment extends BaseFragment {
private IVisitorFlowCallback flowCallback;
public void setFlowCallback(IVisitorFlowCallback callback) {
this.flowCallback = callback;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(ResourceUtils.getResourceID(activity, "R.layout.ug_layout_visitor"), container, false);
TextView btnSwitch = (TextView) ResourceUtils.getViewByParent(view, "R.id.ug_visitor_switch");
btnSwitch.setClickable(true);
TextView btnContinue = (TextView) ResourceUtils.getViewByParent(view, "R.id.ug_visitor_continue");
btnSwitch.setClickable(true);
Button btnUpgrade = (Button) ResourceUtils.getViewByParent(view, "R.id.ug_btn_upgrade");
btnUpgrade.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (flowCallback != null) {
flowCallback.onVisitorUpgradeAccount();
}
}
});
btnSwitch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (flowCallback != null) {
flowCallback.onVisitorSwitchAccount();
}
}
});
btnContinue.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (flowCallback != null) {
flowCallback.onVisitorContinued();
}
}
});
return view;
}
public interface IVisitorFlowCallback {
void onVisitorUpgradeAccount();
void onVisitorSwitchAccount();
void onVisitorContinued();
}
}
三、UI流程控制
UI流程的控制,我们采用每个业务流程对应一个单独的activity。 这样我们可以轻松控制流程中不同fragment的跳转以及流程的生命周期,同时也可以最大化fragment的复用。
我们以游客升级这个的流程为例。 该流程总共有三个UI界面,游客升级提示界面-》点击升级,弹出账户升级界面-》点击升级,弹出输入邮箱验证码界面-》点击确定,发送协议给服务端完成账号升级。
在UGSDK中, 我们定义一个VisitorUpgradeActivity来处理这个流程, 我们先来看下代码:
public class VisitorUpgradeActivity extends BaseFragmentActivity implements
VisitorFragment.IVisitorFlowCallback,
RegisterFragment.IRegisterFlowCallback,
EmailCodeFragment.IEmailCodeFlowCallback {
// 游客账号提示界面
private VisitorFragment visitorFragment;
// 账号升级界面(重用注册界面)
private RegisterFragment upgradeFragment;
// 输入邮箱验证码界面
private EmailCodeFragment emailCodeFragment;
private String email;
private String password;
private String code;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
visitorFragment = findOrCreateFragment(VisitorFragment.class);
upgradeFragment = findOrCreateFragment(RegisterFragment.class);
upgradeFragment.setTitle(ResourceUtils.getString(this, "R.string.ug_visitor_upgrade_title"));
upgradeFragment.setButtonText(ResourceUtils.getString(this, "R.string.ug_visitor_upgrade_btn"));
upgradeFragment.setBackText(ResourceUtils.getString(this, "R.string.ug_visitor_upgrade_cancel"));
emailCodeFragment = findOrCreateFragment(EmailCodeFragment.class);
// 首次启动,防止重复fragment堆叠
if (savedInstanceState == null) {
push(visitorFragment);
}
}
@Override
public void onAttachFragment(Fragment fragment) {
if(fragment instanceof VisitorFragment) {
((VisitorFragment)fragment).setFlowCallback(this);
} else if(fragment instanceof RegisterFragment) {
((RegisterFragment)fragment).setFlowCallback(this);
} else if (fragment instanceof EmailCodeFragment) {
((EmailCodeFragment)fragment).setFlowCallback(this);
}
}
/**
* 游客账号提示界面,点击升级账号,跳转到账号升级界面
*/
@Override
public void onVisitorUpgradeAccount() {
push(upgradeFragment);
}
/**
* 游客账号提示界面,点击切换账号,跳转到登录Activity
*/
@Override
public void onVisitorSwitchAccount() {
// 这里不直接切到登录界面Activity, 直接触发logout,让游戏返回到游戏登录界面,然后再主动登录。
this.finish();
SDKManager.getInstance().onLogout();
}
/**
* 游客账号界面,点击继续,直接关闭该Activity
*/
@Override
public void onVisitorContinued() {
this.finish();
}
/**
* 从账号注册界面返回
*/
@Override
public void onGotoEmailLogin() {
fragmentManager.popBackStack();
}
/**
* 跳转到邮箱验证码输入界面
*/
@Override
public void onGotoEmailVerify(String email, String password) {
this.email = email;
this.password = password;
push(emailCodeFragment);
}
/**
* 从邮箱验证码返回
*/
@Override
public void onEmailCodeBack() {
fragmentManager.popBackStack();
}
/**
* 点击邮箱验证码验证, 账号升级成功。继续游戏
*/
@Override
public void onEmailCodeVerify(String code) {
this.code = code;
doUpgrade();
}
// 发送注册协议
private void doUpgrade() {
UGUser user = SDKManager.getInstance().getLoginedUser();
if(user == null) {
ResourceUtils.showTip(this, "R.string.ug_upgrade_failed_no_login");
return;
}
showLoading();
UserApi.upgrade(user, email, password, code, new IApiListener() {
@Override
public void onSuccess(final UGUser data) {
VisitorUpgradeActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
hideLoading();
VisitorUpgradeActivity.this.finish();
EmailAccountStore.getInstance().saveEmailAccount(new UGEmailAccount(email, password));
SDKManager.getInstance().onUpgradeSuccess(Constants.LoginType.Email, data);
}
});
}
@Override
public void onFailed(final int code, final String msg) {
Log.e(Constants.TAG, "doUpgrade failed. code:" + code + ";msg:" + msg);
VisitorUpgradeActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
hideLoading();
if (code == ApiRequest.R_CODE_FAIL) {
ResourceUtils.showTip(VisitorUpgradeActivity.this, msg);
} else {
ResourceUtils.showTip(VisitorUpgradeActivity.this, "R.string.ug_upgrade_failed");
}
}
});
}
});
}
}
通过上面的代码,我们可以看到, 该流程Activity中, 维护了流程中三个步骤对应的三个fragment:VisitorFragment, RegisterFragment和EmailCodeFragment,并实现了三个Fragment中流程交互对应的Callback。
在onCreate中, 预先初始化了三个fragment,并将第一个步骤游客升级提示fragment给展示了出来。在onAttachFragment中,我们将三个fragment的流程回调监听类都设置为当前activity自身。
下面其他的方法都是对应各个fragment中操作之后对应的流程控制方法了, 在不同的方法中,我们处理流程的跳转或者结束。
最终在最后一步操作,输入邮箱验证码之后,我们像服务端发送请求,完成整个业务流程。
好了,本篇我们介绍了在UGSDK中简化UI和业务流程的控制。