Android Fragment横竖屏切换适配

横竖屏适配最好的方式还是判断设备是平板或手机,进入app时固定展示横屏或者竖屏,设置android:screenOrientation="locked"禁止在app内切换横竖屏。无论什么框架都能适用,且不需要考虑什么兼容。
为了方便横竖屏切换需使用ViewModel和DataBinding双向绑定来控制界面控件的数据和状态,将界面文案、点击事件、是否可见等等存放在ViewModel中,否则需要在Bundle存取大量数据来保证横竖屏切换后界面状态和数据。
横竖屏涉及到Activity和Fragment的复用重建、旧Fragment的关联状态、生命周期的管理避免如网络请求开启线程导致Activity无法释放及请求完成继续操作界面但界面已经销毁导致闪退、界面展示如按钮状态输入文本的内容保存、横屏界面布局和竖屏界面的切换。需要有大局观来考虑各种问题,实现比较复杂。

方法一:保留Activity横竖屏切换重建,Fragment重建,使用mvvm框架实现双向绑定,使得切换后界面显示数据保持一致。缺点就是每次横竖屏切换都会生成新对象,需要做好内存管理。

方法二:Activity不旋转,一个Fragment在同一个布局文件中写两套布局即有横屏又有竖屏界面,区分横竖屏展示。在Fragment的onConfigurationChanged方法中实现横竖屏布局的隐藏和展示。布局中控件状态和文案通过vm来控制,让两个布局中对应控件有相同状态。也可拆分为多个部分,根据横竖屏动态改变控件在布局中的位置。优点是横竖屏共用一个Activity/Fragment对象,缺点是一个xml文件实现两个布局可能内容比较多有上千行。

        <activity
            android:name="com.slaoren.mvvmexamp.ui.MvvmDemo2Activity"
            android:configChanges="orientation|screenSize" 设置不重建activity
            android:screenOrientation="user" />
//MvvmDemo2Activity展示的Fragment
class MvvmConfigurationChangedFragment:BaseFragment<FragmentConfigurationChangedBinding, DemoViewModel>() {
    override fun getLayoutId(): Int {
        return R.layout.fragment_configuration_changed
    }

    override fun setView() {
        mBinding.vm = mViewModel
        //首次进入可以根据横竖屏判断展示什么界面,这里直接展示竖屏
        mBinding.clPortrait.visibility = View.VISIBLE
        mBinding.clLandscape.visibility = View.GONE
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        //横竖屏切换后在此方法中判断展示什么界面
        if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
            Toast.makeText(requireContext(), "竖屏", Toast.LENGTH_SHORT).show()
            mBinding.clPortrait.visibility = View.VISIBLE
            mBinding.clLandscape.visibility = View.GONE
        }else{
            Toast.makeText(requireContext(), "横屏", Toast.LENGTH_SHORT).show()
            mBinding.clPortrait.visibility = View.GONE
            mBinding.clLandscape.visibility = View.VISIBLE
        }
    }
}

布局文件fragment_configuration_changed.xml,在FrameLayout内放置了两个ConstraintLayout,对应横屏展示内容竖屏展示内容,可以根据需要修改实现横竖屏布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="vm"
            type="com.slaoren.mvvmexamp.vm.DemoViewModel" />
    </data>
    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/clPortrait"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/tvTimePortrait"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_margin="10dp"
                android:background="#333333"
                android:gravity="center"
                android:onClick="@{()->vm.clickTime()}"
                android:text="@={vm.time}"
                android:textColor="#ffffff"
                android:textSize="25sp"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintStart_toStartOf="parent"/>

            <TextView
                android:id="@+id/tvPortrait"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Portrait"
                android:textColor="#333333"
                android:textSize="25sp"
                app:layout_constraintTop_toBottomOf="@id/tvTimePortrait"
                app:layout_constraintStart_toStartOf="parent"/>
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/clLandscape"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/tvTimeLandscape"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_marginStart="10dp"
                android:layout_marginEnd="10dp"
                android:layout_marginTop="200dp"
                android:background="#333333"
                android:gravity="center"
                android:onClick="@{()->vm.clickTime()}"
                android:text="@={vm.time}"
                android:textColor="#ffffff"
                android:textSize="25sp"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintStart_toStartOf="parent"/>

            <TextView
                android:id="@+id/tvLandscape"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Landscape"
                android:textColor="#333333"
                android:textSize="25sp"
                app:layout_constraintTop_toBottomOf="@id/tvTimeLandscape"
                app:layout_constraintEnd_toEndOf="parent"/>
        </androidx.constraintlayout.widget.ConstraintLayout>
    </FrameLayout>

</layout>

方法三:Activity不旋转,一个Fragment分别实例化横屏和竖屏两个对象,或者写两个Fragment类分别展示横屏和竖屏的布局。在Activity的onConfigurationChanged方法中实现横竖屏Fragment的对象的切换。通过mvvm和Databinding双向绑定,实现两个Fragment对象间的数据同步。Fragment需要在layout-land、layout中实现两个xml布局对应横竖屏展示。对比方法一减少了创建对象的个数,只创建一个Activity对象,最多一个Fragment只创建两个对象。

        <activity
            android:name="com.slaoren.mvvmexamp.ui.MvvmDemo3Activity"
            android:configChanges="orientation|screenSize" 设置不重建activity
            android:screenOrientation="user" />
//在MvvmDemo3Activity的方法onConfigurationChanged中根据当前横竖屏状态,切换对应Fragment对象。另外有多个界面展示有不同Fragment时需要考虑记录下当前展示哪个界面的Fragment。
    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        Log.d("test", "onConfigurationChanged:"+newConfig.orientation)

        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
            Toast.makeText(this, "横屏", Toast.LENGTH_SHORT).show()
            changeFragment("landscapeFragment")
        }else{
            Toast.makeText(this, "竖屏", Toast.LENGTH_SHORT).show()
            changeFragment("portraitFragment")
        }
    }

//切换Fragment,首次展示时创建Fragment实例,并隐藏不需要展示的Fragment
    private fun changeFragment(tag: String) {
        val fragmentManager = supportFragmentManager
        val fragmentTransaction = fragmentManager.beginTransaction()
        for (fragmentManagerFragment in fragmentManager.fragments) {
            fragmentTransaction.hide(fragmentManagerFragment)
        }
        var fragment = fragmentManager.findFragmentByTag(tag)
        if (fragment != null) {
            fragmentTransaction.show(fragment)
        } else {
            fragment = when(tag){
                "landscapeFragment"->MvvmDemoFragment()//只展示layout-land布局
                "portraitFragment"->MvvmDemoFragment()//只展示layout布局
                //其他Fragment界面横竖屏对象
                else -> null
            }

            if (fragment != null) {
                fragmentTransaction.add(R.id.frameLayout, fragment, tag)
            }
        }
        fragmentTransaction.commitNow()
    }

Fragment相关

class MvvmDemoFragment:BaseFragment<FragmentMvvmDemoBinding, DemoViewModel>() {
    override fun getLayoutId(): Int {
        return R.layout.fragment_mvvm_demo
    }

    override fun setView() {
        mBinding.vm = mViewModel
    }

}

系统会根据当前手机横竖屏状态读取对应layout或layout-land中的fragment_mvvm_demo.xml文件,注意两个xml文件的具体实现为横屏展示和竖屏展示
在这里插入图片描述

附上demo示例地址

  • 11
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值