单Activity、多Fragment架构可以保证App具有极高的流畅度,因为通过多个Fragment切换实现页面跳转的代价远低于通过多个Activity切换实现页面跳转的代价。这里使用一个统一的Activity来管理多个Fragment。
为了提高开发效率,降低出错概率,我选择集成非常流行的Fragmentation库来处理有关Fragment的事务。
ProxyActivity实现Fragmentation提供的ISupportActivity接口,作为所有Fragment的容器。ProxyActivity类是抽象类。App中唯一的Activity将继承自ProxyActivity。
import android.annotation.SuppressLint; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.ContentFrameLayout; import com.qilu.core.R; import com.qilu.core.delegates.QiluDelegate; import me.yokeyword.fragmentation.ExtraTransaction; import me.yokeyword.fragmentation.ISupportActivity; import me.yokeyword.fragmentation.SupportActivityDelegate; import me.yokeyword.fragmentation.anim.DefaultHorizontalAnimator; import me.yokeyword.fragmentation.anim.FragmentAnimator; public abstract class ProxyActivity extends AppCompatActivity implements ISupportActivity { private final SupportActivityDelegate DELEGATE = new SupportActivityDelegate(this); public abstract QiluDelegate setRootDelegate(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); DELEGATE.onCreate(savedInstanceState); initContainer(savedInstanceState); } private void initContainer(@Nullable Bundle savedInstanceState) { @SuppressLint("RestrictedApi") final ContentFrameLayout container = new ContentFrameLayout(this); container.setId(R.id.delegate_container); setContentView(container); if (savedInstanceState == null) { DELEGATE.loadRootFragment(R.id.delegate_container, setRootDelegate()); } } @Override protected void onDestroy() { DELEGATE.onDestroy(); super.onDestroy(); System.gc(); System.runFinalization(); } @Override public SupportActivityDelegate getSupportDelegate() { return DELEGATE; } @Override public ExtraTransaction extraTransaction() { return DELEGATE.extraTransaction(); } @Override public FragmentAnimator getFragmentAnimator() { return DELEGATE.getFragmentAnimator(); } @Override public void setFragmentAnimator(FragmentAnimator fragmentAnimator) { DELEGATE.setFragmentAnimator(new DefaultHorizontalAnimator()); } @Override public FragmentAnimator onCreateFragmentAnimator() { return DELEGATE.onCreateFragmentAnimator(); } @Override public void onBackPressedSupport() { DELEGATE.onBackPressedSupport(); } @Override public void onBackPressed() { DELEGATE.onBackPressed(); } }由于我对Fragment进行了封装,项目中的所有Fragment也改称呼为Delegate。 项目中所有Delegate的基类是BaseDelegate。 BaseDelegate实现了Fragmentation提供的ISupportFragment接口。
import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.support.annotation.IdRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import com.qilu.core.activities.ProxyActivity; import me.yokeyword.fragmentation.ExtraTransaction; import me.yokeyword.fragmentation.ISupportFragment; import me.yokeyword.fragmentation.SupportFragmentDelegate; import me.yokeyword.fragmentation.anim.FragmentAnimator; public abstract class BaseDelegate extends Fragment implements ISupportFragment { private final SupportFragmentDelegate DELEGATE = new SupportFragmentDelegate(this); private View mRootView = null; public abstract Object setLayout(); public abstract void onBindView(@Nullable Bundle savedInstanceState, @NonNull View rootView); public <T extends View> T $(@IdRes int viewId) { if (mRootView != null) { return mRootView.findViewById(viewId); } throw new NullPointerException("rootView is null"); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { final View rootView; if (setLayout() instanceof Integer) { rootView = inflater.inflate((int) setLayout(), container, false); } else if (setLayout() instanceof View) { rootView = (View) setLayout(); } else { throw new ClassCastException("type of setLayout() must be int or View!"); } mRootView = rootView; onBindView(savedInstanceState, rootView); return rootView; } public final ProxyActivity getProxyActivity() { return (ProxyActivity) _mActivity; } @Override public void post(Runnable runnable) { DELEGATE.post(runnable); } protected FragmentActivity _mActivity; @Override public SupportFragmentDelegate getSupportDelegate() { return DELEGATE; } @Override public ExtraTransaction extraTransaction() { return DELEGATE.extraTransaction(); } @Override public void onAttach(Context context) { super.onAttach(context); DELEGATE.onAttach((Activity) context); _mActivity = DELEGATE.getActivity(); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); DELEGATE.onCreate(savedInstanceState); } @Override public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { return DELEGATE.onCreateAnimation(transit, enter, nextAnim); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); DELEGATE.onActivityCreated(savedInstanceState); } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); DELEGATE.onSaveInstanceState(outState); } @Override public void onResume() { super.onResume(); DELEGATE.onResume(); } @Override public void onPause() { super.onPause(); DELEGATE.onPause(); } @Override public void onDestroyView() { DELEGATE.onDestroyView(); super.onDestroyView(); } @Override public void onDestroy() { DELEGATE.onDestroy(); super.onDestroy(); } @Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); DELEGATE.onHiddenChanged(hidden); } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); DELEGATE.setUserVisibleHint(isVisibleToUser); } @Override public void enqueueAction(Runnable runnable) { DELEGATE.enqueueAction(runnable); } @Override public void onEnterAnimationEnd(Bundle savedInstanceState) { DELEGATE.onEnterAnimationEnd(savedInstanceState); } @Override public void onLazyInitView(@Nullable Bundle savedInstanceState) { DELEGATE.onLazyInitView(savedInstanceState); } @Override public void onSupportVisible() { DELEGATE.onSupportVisible(); } @Override public void onSupportInvisible() { DELEGATE.onSupportInvisible(); } @Override final public boolean isSupportVisible() { return DELEGATE.isSupportVisible(); } @Override public FragmentAnimator onCreateFragmentAnimator() { return DELEGATE.onCreateFragmentAnimator(); } @Override public FragmentAnimator getFragmentAnimator() { return DELEGATE.getFragmentAnimator(); } @Override public void setFragmentAnimator(FragmentAnimator fragmentAnimator) { DELEGATE.setFragmentAnimator(fragmentAnimator); } @Override public boolean onBackPressedSupport() { return DELEGATE.onBackPressedSupport(); } @Override public void setFragmentResult(int resultCode, Bundle bundle) { DELEGATE.setFragmentResult(resultCode, bundle); } @Override public void onFragmentResult(int requestCode, int resultCode, Bundle data) { DELEGATE.onFragmentResult(requestCode, resultCode, data); } @Override public void onNewBundle(Bundle args) { DELEGATE.onNewBundle(args); } @Override public void putNewBundle(Bundle newBundle) { DELEGATE.putNewBundle(newBundle); } }在6.0版本以后,Android引入动态权限申请机制。 因此,我设计了一个中间层,即PermissionCheckerDelegate,完成有关权限申请的判定。PermissionCheckerDelegate继承自BaseDelegate,也是一个抽象基类。
import android.Manifest; import android.content.Intent; import android.net.Uri; import android.support.annotation.NonNull; import android.support.v7.app.AlertDialog; import android.widget.Toast; import com.qilu.core.ui.camera.CameraImageBean; import com.qilu.core.ui.camera.QiluCamera; import com.qilu.core.ui.camera.RequestCodes; import com.qilu.core.util.callback.CallbackManager; import com.qilu.core.util.callback.CallbackType; import com.qilu.core.util.callback.IGlobalCallback; import com.yalantis.ucrop.UCrop; import permissions.dispatcher.NeedsPermission; import permissions.dispatcher.OnNeverAskAgain; import permissions.dispatcher.OnPermissionDenied; import permissions.dispatcher.OnShowRationale; import permissions.dispatcher.PermissionRequest; import permissions.dispatcher.RuntimePermissions; @RuntimePermissions public abstract class PermissionCheckerDelegate extends BaseDelegate { // 默认的最大照片尺寸 private int maxWidth=400; private int maxHeight=400; //不是直接调用方法 @NeedsPermission(Manifest.permission.CAMERA) void startCamera() { QiluCamera.start(this); } //这个是真正调用的方法 public void startCameraWithCheck(int maxWidth, int maxHeight) { this.maxHeight=maxHeight; this.maxWidth=maxWidth; PermissionCheckerDelegatePermissionsDispatcher.startCameraWithPermissionCheck(this); } public void startCameraWithCheck() { PermissionCheckerDelegatePermissionsDispatcher.startCameraWithPermissionCheck(this); } @OnPermissionDenied(Manifest.permission.CAMERA) void onCameraDenied() { Toast.makeText(getContext(), "不允许拍照", Toast.LENGTH_LONG).show(); } @OnNeverAskAgain(Manifest.permission.CAMERA) void onCameraNever() { Toast.makeText(getContext(), "永久拒绝权限", Toast.LENGTH_LONG).show(); } @OnShowRationale(Manifest.permission.CAMERA) void onCameraRationale(PermissionRequest request) { showRationaleDialog(request); } private void showRationaleDialog(final PermissionRequest request) { if (getContext() != null) { new AlertDialog.Builder(getContext()) .setPositiveButton("同意使用", (dialog, which) -> request.proceed()) .setNegativeButton("拒绝使用", (dialog, which) -> request.cancel()) .setCancelable(false) .setMessage("权限管理") .show(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); PermissionCheckerDelegatePermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case RequestCodes.TAKE_PHOTO: final Uri resultUri = CameraImageBean.getInstance().getPath(); if (getContext() != null) { UCrop.of(resultUri, resultUri) .withMaxResultSize(maxWidth, maxHeight) .start(getContext(), this); } break; case RequestCodes.PICK_PHOTO: if (data != null) { final Uri pickPath = data.getData(); //从相册选择后需要有个路径存放剪裁过的图片 final String pickCropPath = QiluCamera.createCropFile().getPath(); if (pickPath != null && getContext() != null) { UCrop.of(pickPath, Uri.parse(pickCropPath)) .withMaxResultSize(maxWidth, maxHeight) .start(getContext(), this); } } break; case RequestCodes.CROP_PHOTO: final Uri cropUri = UCrop.getOutput(data); //拿到剪裁后的数据进行处理 @SuppressWarnings("unchecked") final IGlobalCallback<Uri> callback = CallbackManager .getInstance() .getCallback(CallbackType.ON_CROP); if (callback != null) { callback.executeCallback(cropUri); } break; case RequestCodes.CROP_ERROR: Toast.makeText(getContext(), "剪裁出错", Toast.LENGTH_SHORT).show(); break; default: break; } } } }QiluDelegate是项目中所有Delegate类型实例的直接父类。 QiluDelegate继承自PermissionCheckerDelegate。
public abstract class QiluDelegate extends PermissionCheckerDelegate { @SuppressWarnings("unchecked") public <T extends QiluDelegate> T getParentDelegate() { return (T) getParentFragment(); } }