該文章講解的源碼全憑個人積累的經(jīng)驗編寫,所以如果有大神覺得不好或者有更好的意見希望可以提出來,謝謝!
本文的適讀人群
- 從 zhongjhATC/AlbumCameraRecorder 過來了解如何更深入的自定義Camera布局
- 想了解如何優(yōu)化復(fù)雜UI的類
- 想了解如何封裝父類、抽象類作為組件易于后續(xù)擴展
該源碼類是什么功能,如何復(fù)雜法?
功能可以理解為是微信的拍攝、錄制結(jié)合的界面,甚至支持多圖,多視頻錄制。具體功能可點擊zhongjhATC/AlbumCameraRecorder 查看。
那么我們可以初步理解為有以下幾個核心功能:
- 圖片功能
- 視頻功能
- 拍攝、錄制等功能
以上圖片功能和視頻功能又是只通過一個按鈕觸發(fā),那么很多UI邏輯都會糅合在一個UI類里面。所以,這次代碼優(yōu)化選擇了狀態(tài)模式 + Facade模式。
同時,考慮到更多開發(fā)者使用該庫時會需要擴展不同的邏輯業(yè)務(wù),所以,這些類都要弄成可繼承、可擴展使用的。
讓開發(fā)者更好了解如何使用,就出現(xiàn)了此文章。
我們先直接看優(yōu)化后的CameraFragment如何擴展
- 自定義一個CameraFragment,需要繼承BaseCameraFragment,代碼如下:
public class CameraFragment1 extends BaseCameraFragment<CameraStateManagement, BaseCameraPicturePresenter, BaseCameraVideoPresenter> {
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èn)的就已經(jīng)滿足了,就直接使用R.layout.fragment_camera_zjh),然后在一些NonNull標(biāo)記下必須提供不為null的View或者類,接著就可以直接在自己的布局上增加自己想要的View了,想擴展的地方可以覆寫方法添加邏輯。
當(dāng)一個Fragment弄好后,我們需要告訴該庫使用該Fragment,代碼如下:
cameraSetting.setBaseCameraFragment(CameraFragment1.newInstance());
那么在這里就結(jié)束了,當(dāng)然,如果你需要擴展更多的東西,我們需要更深入了解其他類的作用。讓我們繼續(xù)往下看
我們看看優(yōu)化后的代碼包結(jié)構(gòu)
camera -->
BaseCameraFragment
adapter-->
impl-->
presenter-->
PicturePresenter
VideoPresenter
state-->
StateInterface
StateMode
CameraStateManagement
type-->
PictureComplete
PictureMultiple
Preview
VideoComplete
VideoIn
VideoMultiple
VideoMultipleIn
以表格形式講解每一個包結(jié)構(gòu)
| 包結(jié)構(gòu) | 作用 |
|---|---|
| BaseCameraFragment | 一個父類,提供于開發(fā)者繼承擴展該類,該類可認(rèn)為是個大容器,可使用其他類 |
| adapter | 顧名思義,只是個adpater,目前只存放顯示多圖的適配器 |
| impl | 包含所有接口,主要是用來告訴開發(fā)者都有哪些方法可以擴展使用 |
| presenter | 并不是MVP的presenter,別混淆了。該presenter含有picture和video,picture是處理所有有關(guān)圖片UI的邏輯,而video是處理所有有關(guān)視頻UI的邏輯 |
| state | 狀態(tài)模式,StateInterface告訴開發(fā)者狀態(tài)類有哪些方法可以擴展使用,StateMode是個實體,CameraStateManagement則是管理當(dāng)前狀態(tài),進行相關(guān)UI邏輯。而type里面的PictureComplete、PictureMultiple…等則是各自狀態(tài)處理他們當(dāng)前狀態(tài)的UI邏輯。 |
那么了解過后,會發(fā)現(xiàn),其實是三個實現(xiàn)類分解了這個UI類所包含的功能,腦圖:

接口
接口在這里只起到規(guī)范性作用,單純是讓其他沒接觸過的開發(fā)者可以一目了然了解這些類有什么方法。
這段代碼可以看到實現(xiàn)了兩個接口,區(qū)分兩個接口讓開發(fā)者更好分辨接口的方法屬于哪一類的
public abstract class BaseCameraFragment
extends BaseFragment implements ICameraView, ICameraFragment
以表格形式講解每一個接口類
| 接口 | 作用 | 鏈接 |
|---|---|---|
| ICameraFragment | 拍攝界面的接口,主要用于告示開發(fā)者可以使用哪些方法,大部分是關(guān)于View操作的界面邏輯,除了圖片、視頻、實例化View,其他方法都統(tǒng)一在Fragment使用 | ICameraFragment |
| ICameraView | 錄制界面規(guī)定view的設(shè)置。對所有View都標(biāo)記了NonNull和Nullable。標(biāo)記了NonNull的View返回是不能為空的,在布局上必須使用這些View,當(dāng)然,也可以繼承View加上你想要的方法 | ICameraView |
| ICameraPicture | 拍攝界面的有關(guān)圖片View的接口,控制多圖Adapter也是在這里實現(xiàn) | ICameraPicture |
| ICameraVideo | 拍攝界面的有關(guān)視頻View的接口 | ICameraVideo |
| IState | 狀態(tài)事件接口,對于不同狀態(tài)下,他們各自的實現(xiàn)不一樣 | IState |
使用泛型+父類+抽象方法 規(guī)范開發(fā)者使用
從上面得知,我們CameraFragment封裝出了圖片、視頻、狀態(tài)三大類出去,那么如何規(guī)定開發(fā)者必須引用這三大類進來呢?代碼如下:
public abstract class BaseCameraFragment
<StateManagement extends CameraStateManagement,
CameraPicture extends BaseCameraPicturePresenter,
CameraVideo extends BaseCameraVideoPresenter> {
@NonNull
public abstract StateManagement getCameraStateManagement();
@NonNull
public abstract CameraPicture getCameraPicturePresenter();
@NonNull
public abstract CameraVideo getCameraVideoPresenter();
}
那么在子類繼承BaseCameraFragment得時候,必須實現(xiàn)這三個類
public class CameraFragment1 extends BaseCameraFragment<CameraStateManagement, BaseCameraPicturePresenter, BaseCameraVideoPresenter> {
BaseCameraPicturePresenter cameraPicturePresenter = new BaseCameraPicturePresenter(this);
@NonNull
@Override
public BaseCameraPicturePresenter getCameraPicturePresenter() {
return cameraPicturePresenter;
}
}
從上面三個實現(xiàn)方法得知,如果你想自定義有關(guān)圖片邏輯或者視頻邏輯,那么需要繼承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(), "拍照時觸發(fā)自定義事件!", Toast.LENGTH_SHORT).show();
}
}
開發(fā)者使用自定義CameraLayout過程碰到的疑問
-
1.我想動態(tài)加點數(shù)據(jù),這些數(shù)據(jù)都是來自于上一個Activity
答:可以通過自定義的CameraFragment.setArguments()動態(tài)賦值,也可以使用event之類的,反正就當(dāng)一個普通的fragment使用。
-
-
我想在關(guān)閉前做些事 為什么設(shè)置完監(jiān)聽 關(guān)閉不掉了mBinding.imgClose.setOnClickListener(v -> { // 自定義代碼 })
答:因為你關(guān)閉事件覆蓋掉了,要熟悉基類的本身事件。正確做法是使用基類的有關(guān)關(guān)閉事件方法,再加上你的自定義事件。(多了解基類)
-
寫在最后
那么基本在這里就差不多介紹結(jié)束了,想了解更加具體的做法和源碼的,請下載下面的GitHub源碼鏈接