该文章讲解的源码全凭个人积累的经验编写,所以如果有大神觉得不好或者有更好的意见希望可以提出来,谢谢!
本文的适读人群
- 从 zhongjhATC/AlbumCameraRecorder 过来了解如何更深入的自定义Camera布局
- 想了解如何优化复杂UI的类
- 想了解如何封装父类、抽象类作为组件易于后续扩展
该源码类是什么功能,如何复杂法?
功能可以理解为是微信的拍摄、录制结合的界面,甚至支持多图,多视频录制。具体功能可点击zhongjhATC/AlbumCameraRecorder 查看。
那么我们可以初步理解为有以下几个核心功能:
- 图片功能
- 视频功能
- 拍摄、录制等功能
以上图片功能和视频功能又是只通过一个按钮触发,那么很多UI逻辑都会糅合在一个UI类里面。所以,这次代码优化选择了状态模式 + Facade模式。
同时,考虑到更多开发者使用该库时会需要扩展不同的逻辑业务,所以,这些类都要弄成可继承、可扩展使用的。
让开发者更好了解如何使用,就出现了此文章。
我们先直接看优化后的CameraFragment如何扩展
- 自定义一个CameraFragment,需要继承BaseCameraFragment,代码如下:
public class CameraFragment1 extends BaseCameraFragment {
FragmentCamera1Binding mBinding;
BaseCameraPicturePresenter cameraPicturePresenter = new BaseCameraPicturePresenter(this);
BaseCameraVideoPresenter cameraVideoPresenter = new BaseCameraVideoPresenter(this);
CameraStateManagement cameraStateManagement = new CameraStateManagement(this);
public static CameraFragment1 newInstance() {
return new CameraFragment1();
}
@Override
public View setContentView(LayoutInflater inflater, ViewGroup container) {
mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_camera1,container,false);
return mBinding.getRoot();
}
@Override
public void initView(View view, Bundle savedInstanceState) {
}
/**
* TODO
* 覆写该事件,赋值自定义按钮事件
*/
@Override
protected void initListener() {
super.initListener();
mBinding.btnCustom.setOnClickListener(v -> Toast.makeText(getMyContext(), "我是自定义的", Toast.LENGTH_SHORT).show());
}
@NonNull
@Override
public IChildClickableLayout getChildClickableLayout() {
return mBinding.rlMain;
}
@Nullable
@Override
public View getTopView() {
return mBinding.clMenu;
}
@NonNull
@Override
public CameraView getCameraView() {
return mBinding.cameraView;
}
@Override
public RecyclerView getRecyclerViewPhoto() {
return mBinding.rlPhoto;
}
@Nullable
@Override
public View[] getMultiplePhotoView() {
return new View[]{mBinding.vLine1, mBinding.vLine2};
}
@NonNull
@Override
public PhotoVideoLayout getPhotoVideoLayout() {
return mBinding.pvLayout;
}
@NonNull
@Override
public com.zhongjh.albumcamerarecorder.widget.ImageViewTouch getSinglePhotoView() {
return mBinding.imgPhoto;
}
@Nullable
@Override
public View getCloseView() {
return mBinding.imgClose;
}
@Nullable
@Override
public ImageView getFlashView() {
return mBinding.imgFlash;
}
@Nullable
@Override
public ImageView getSwitchView() {
return mBinding.imgSwitch;
}
@NonNull
@Override
public CameraStateManagement getCameraStateManagement() {
return cameraStateManagement;
}
@NonNull
@Override
public BaseCameraPicturePresenter getCameraPicturePresenter() {
return cameraPicturePresenter;
}
@NonNull
@Override
public BaseCameraVideoPresenter getCameraVideoPresenter() {
return cameraVideoPresenter;
}
}
从上面代码看到,我们提供一个View布局(如果默认的就已经满足了,就直接使用R.layout.fragment_camera_zjh
),然后在一些NonNull标记下必须提供不为null的View或者类,接着就可以直接在自己的布局上增加自己想要的View了,想扩展的地方可以覆写方法添加逻辑。
当一个Fragment弄好后,我们需要告诉该库使用该Fragment,代码如下:
cameraSetting.setBaseCameraFragment(CameraFragment1.newInstance());
那么在这里就结束了,当然,如果你需要扩展更多的东西,我们需要更深入了解其他类的作用。让我们继续往下看
我们看看优化后的代码包结构
camera -->
BaseCameraFragment
adapter-->
impl-->
presenter-->
PicturePresenter
VideoPresenter
state-->
StateInterface
StateMode
CameraStateManagement
type-->
PictureComplete
PictureMultiple
Preview
VideoComplete
VideoIn
VideoMultiple
VideoMultipleIn
以表格形式讲解每一个包结构
包结构 | 作用 |
---|---|
BaseCameraFragment | 一个父类,提供于开发者继承扩展该类,该类可认为是个大容器,可使用其他类 |
adapter | 顾名思义,只是个adpater,目前只存放显示多图的适配器 |
impl | 包含所有接口,主要是用来告诉开发者都有哪些方法可以扩展使用 |
presenter | 并不是MVP的presenter,别混淆了。该presenter含有picture和video,picture是处理所有有关图片UI的逻辑,而video是处理所有有关视频UI的逻辑 |
state | 状态模式,StateInterface告诉开发者状态类有哪些方法可以扩展使用,StateMode是个实体,CameraStateManagement则是管理当前状态,进行相关UI逻辑。而type里面的PictureComplete、PictureMultiple…等则是各自状态处理他们当前状态的UI逻辑。 |
那么了解过后,会发现,其实是三个实现类分解了这个UI类所包含的功能,脑图:
接口
接口在这里只起到规范性作用,单纯是让其他没接触过的开发者可以一目了然了解这些类有什么方法。
这段代码可以看到实现了两个接口,区分两个接口让开发者更好分辨接口的方法属于哪一类的
public abstract class BaseCameraFragment
extends BaseFragment implements ICameraView, ICameraFragment
以表格形式讲解每一个接口类
接口 | 作用 | 链接 |
---|---|---|
ICameraFragment | 拍摄界面的接口,主要用于告示开发者可以使用哪些方法,大部分是关于View操作的界面逻辑,除了图片、视频、实例化View,其他方法都统一在Fragment使用 | ICameraFragment |
ICameraView | 录制界面规定view的设置。对所有View都标记了NonNull和Nullable。标记了NonNull的View返回是不能为空的,在布局上必须使用这些View,当然,也可以继承View加上你想要的方法 | ICameraView |
ICameraPicture | 拍摄界面的有关图片View的接口,控制多图Adapter也是在这里实现 | ICameraPicture |
ICameraVideo | 拍摄界面的有关视频View的接口 | ICameraVideo |
IState | 状态事件接口,对于不同状态下,他们各自的实现不一样 | IState |
使用泛型+父类+抽象方法 规范开发者使用
从上面得知,我们CameraFragment封装出了图片、视频、状态三大类出去,那么如何规定开发者必须引用这三大类进来呢?代码如下:
public abstract class BaseCameraFragment
{
@NonNull
public abstract StateManagement getCameraStateManagement();
@NonNull
public abstract CameraPicture getCameraPicturePresenter();
@NonNull
public abstract CameraVideo getCameraVideoPresenter();
}
那么在子类继承BaseCameraFragment得时候,必须实现这三个类
public class CameraFragment1 extends BaseCameraFragment {
BaseCameraPicturePresenter cameraPicturePresenter = new BaseCameraPicturePresenter(this);
@NonNull
@Override
public BaseCameraPicturePresenter getCameraPicturePresenter() {
return cameraPicturePresenter;
}
}
从上面三个实现方法得知,如果你想自定义有关图片逻辑或者视频逻辑,那么需要继承Presenter扩展你想要的方法后使用它,代码如下:
public class CameraPicturePresenter extends BaseCameraPicturePresenter {
public CameraPicturePresenter(BaseCameraFragment extends CameraStateManagement, ? extends BaseCameraPicturePresenter, ? extends BaseCameraVideoPresenter> baseCameraFragment) {
super(baseCameraFragment);
}
@Override
public void takePhoto() {
super.takePhoto();
Toast.makeText(baseCameraFragment.getMyContext(), "拍照时触发自定义事件!", Toast.LENGTH_SHORT).show();
}
}
开发者使用自定义CameraLayout过程碰到的疑问
-
1.我想动态加点数据,这些数据都是来自于上一个Activity
答:可以通过自定义的CameraFragment.setArguments()动态赋值,也可以使用event之类的,反正就当一个普通的fragment使用。
-
-
我想在关闭前做些事 为什么设置完监听 关闭不掉了mBinding.imgClose.setOnClickListener(v -> { // 自定义代码 })
答:因为你关闭事件覆盖掉了,要熟悉基类的本身事件。正确做法是使用基类的有关关闭事件方法,再加上你的自定义事件。(多了解基类)
-
写在最后
那么基本在这里就差不多介绍结束了,想了解更加具体的做法和源码的,请下载下面的GitHub源码链接
- 觉得好用的欢迎给个Star(GitHub)
- 发现任何BUG欢迎留言或者留个Issues(Issues)
- 任何转载请注明出处