【Android】创建基类BaseActivity和BaseFragment
目的
在 Android 开发中,创建基类的 Activity 和 Fragment 主要是为了提高代码的复用性、可维护性和一致性。通过定义基类,可以将多个 Activity 和 Fragment 中共享的功能和行为提取出来,避免重复代码,从而简化开发过程。
项目需求
一个项目里面是Java代码编写,然后现在引入Kotlin 进行开发,但是里面的原来的代码还是尽量不要动。
需求实现
现在有两种实现UI的布局,一种是使用ViewBinding的,一种是不使用的。
需要在基类里面进行处理。
项目代码
abstract class BaseActivity<VB : ViewBinding, VM : ViewModel> : AppCompatActivity() {
protected var binding: VB? = null
protected var viewModel: VM? = null
private val activityList = arrayListOf<Activity>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MyApplication.activityTasks.add(this)
binding = getMyViewBinding()
if (binding != null) {
setContentView(binding!!.root)
} else {
setContentView(getContentViewId())
}
viewModel = getMyViewModel()
val toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar?.let {
setSupportActionBar(it)
supportActionBar?.setDisplayShowTitleEnabled(false)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
initToolbar(ToolbarHelper(toolbar))
}
initData()
activityList.add(this)
}
/**
* 修复全屏页面状态下因为某些操作导致UI改变之后的全屏显示失效问题
*/
override fun onWindowFocusChanged(hasFocus: Boolean) {
val decorView = window.decorView
decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
super.onWindowFocusChanged(hasFocus)
}
override fun onDestroy() {
MyApplication.activityTasks.remove(this)
activityList.remove(this)
super.onDestroy()
binding = null
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
finish()
}
return super.onOptionsItemSelected(item)
}
//跳转到指定activity
open fun startActivity(cls: Class<out Activity?>?) {
startActivity(Intent(this, cls))
}
open fun clearAllActivity() {
for (activity in activityList) {
activity.finish()
}
activityList.clear()
}
/**
* Binding布局,使用ViewBinding的话要重写这个方法
*/
protected open fun getMyViewBinding(): VB? {
return null
}
/**
* 普通布局,不使用ViewBinding的话要重写这个方法
*/
protected open fun getContentViewId(): Int {
return 0
}
/**
* 默认返回 null,可以让子类选择是否使用 ViewModel
*/
protected open fun getMyViewModel(): VM? {
return null
}
/**
* 初始化数据,子类必须实现这个抽象方法
*/
protected abstract fun initData()
/**
* 使用ActionBar要重写这个方法
*/
protected open fun initToolbar(toolbarHelper: ToolbarHelper) {}
/**
* 添加或切换Fragment的方法
* 这个是可以添加和切换,用于多个Fragment管理的
*/
fun addOrReplaceFragment(containerId: Int, fragment: Fragment) {
// 获取FragmentManager
val transaction = supportFragmentManager.beginTransaction()
// 检查该Fragment是否已经存在
val existing = supportFragmentManager.findFragmentByTag(fragment::class.java.simpleName)
if (existing == null) {
// 如果Fragment不存在,则添加
transaction.replace(containerId, fragment, fragment::class.java.simpleName)
//fragmentTransaction.addToBackStack(fragment::class.java.simpleName) // 添加到回退栈
} else {
// 如果Fragment已经存在,则切换到该Fragment
transaction.show(existing)
supportFragmentManager.fragments.forEach { frag ->
if (frag != existing) {
transaction.hide(frag) // 隐藏其他Fragment
}
}
}
// 提交事务
transaction.commit()
}
这样一个可以满足大多数需求的基类就好了,如果是一个不使用ViewBinding的子类的话,直接就可以这样写
public class ConnectionActivity extends BaseActivity {
@Override
protected int getContentViewId() {
return R.layout.activity_coodinate_system;
}
@Override
protected void initData() {
}
@Override
protected void initToolbar(ToolbarHelper toolbarHelper) {
toolbarHelper.setTitle(getString(R.string.connectSetting));
}
但是要是使用ViewBinding的话,就这样写
public class SysSettingActivity extends BaseActivity<ActivitySysSettingBinding, SystemViewModel> {
@Nullable
@Override
protected ActivitySysSettingBinding getMyViewBinding() {
return ActivitySysSettingBinding.inflate(getLayoutInflater());
}
@Nullable
@Override
protected SystemViewModel getMyViewModel() {
return new ViewModelProvider(this).get(SystemViewModel.class);
}
@Override
protected void initToolbar(@NonNull ToolbarHelper toolbarHelper) {
toolbarHelper.setTitle(getString(R.string.sys_setting));
}
@Override
protected void initData() {
}
}
注意:要是使用Kotlin写子类,但是又不使用ViewBinding的话,子类需要这样写
class WelcomeActivity : BaseActivity<Nothing, Nothing>(){
override fun getContentViewId(): Int {
return R.layout.activity_welcome
}
override fun initData() {
}
}
同样的,基类的BaseFragment写法也是很相似的
abstract class BaseFragment<VB : ViewBinding, VM : ViewModel> : Fragment() {
protected var binding: VB? = null
protected var viewModel: VM? = null
private var rootView: View? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
getOnCreateView()
binding = getMyViewBinding()
if (binding != null) {
return binding!!.root
} else {
rootView = inflater.inflate(getMYLayoutId(), null)
return rootView
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = getMyViewModel()
initData()
}
/**
* 只给有需要的使用,在 onCreateView 里面使用的
*/
protected open fun getOnCreateView() {}
/**
* Binding布局,使用ViewBinding的话要重写这个方法
*/
protected open fun getMyViewBinding(): VB? {
return null
}
/**
* 普通布局,不使用ViewBinding的话要重写这个方法
*/
protected open fun getMYLayoutId(): Int {
return 0
}
/**
* 在普通布局时获取View
*/
protected open fun getMyView(): View? {
return rootView;
}
/**
* 默认返回 null,可以让子类选择是否使用 ViewModel
*/
protected open fun getMyViewModel(): VM? {
return null
}
/**
* 初始化数据
*/
protected abstract fun initData()
//跳转到指定activity
open fun startActivity(cls: Class<out Activity?>?) {
startActivity(Intent(context, cls))
}
}
后面我们也可以根据自己的项目需求,在基类里面加入一些其他的功能代码,比如权限判定啊、加载框等等。