纵向上的 Viewpager 和横向上的两个窗格
Viewpager on portrait and two panes on landscape
我正在尝试实现一种布局,当设备纵向显示时显示一个视图寻呼机,当设备横向显示时显示两个窗格。
所以我做了两个不同的布局文件,一个只有一个ViewPager,一个有一个LinearLayout,一个有两个FrameLayout,我觉得没必要在这里展示。这两个配置还有一个布尔值hasTwoPanes
。
@Inject FragmentOne fragmentOne;
@Inject FragmentTwo fragmentTwo;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
boolean hasTwoPanes = getResources().getBoolean(R.bool.hasTwoPanes);
TabLayout tabLayout = findViewById(R.id.tab_layout);
ViewPager viewPager = findViewById(R.id.view_pager);
if (hasTwoPanes) {
tabLayout.setVisibility(View.GONE);
} else {
tabLayout.setVisibility(View.VISIBLE);
tabLayout.setupWithViewPager(viewPager);
viewPager.setAdapter(new MyPagerAdapter(fm));
}
FragmentOne frag1 = (FragmentOne) fm.findFragmentByTag(getFragmentName(0));
if (frag1 != null) fragmentOne = frag1;
FragmentTwo frag2 = (FragmentTwo) fm.findFragmentByTag(getFragmentName(1));
if (frag2 != null) fragmentTwo = frag2;
if (hasTwoPanes) {
if (frag1 != null) {
fm.beginTransaction().remove(fragmentOne).commit();
fm.beginTransaction().remove(fragmentTwo).commit();
fm.executePendingTransactions();
}
fm.beginTransaction().add(R.id.frame_frag1, fragmentOne, getFragmentName(0)).commit();
fm.beginTransaction().add(R.id.frame_frag2, fragmentTwo, getFragmentName(1)).commit();
}
}
private class MyPagerAdapter extends FragmentPagerAdapter {
MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
if (position == 0) {
return fragmentOne;
} else {
return fragmentTwo;
}
}
@Override
public int getCount() {
return 2;
}
}
private static String getFragmentName(int pos) {
return "android:switcher:" + R.id.view_pager + ":" + pos;
}
两个片段被注入了Dagger。如果不存在任何片段,这些注入的片段将根据方向添加到视图寻呼机或布局中。
因为视图寻呼机适配器为其片段命名,我需要知道该名称(因此 getFragmentName(int pos)
方法)才能在旋转后取回该片段。
结果是从纵向旋转到横向时状态正确恢复,但是当从横向旋转到纵向时,视图寻呼机完全是空的。当我旋转回横向时,碎片重新出现。选项卡布局也有问题,没有滑动动画,我只能从一个选项卡连续滑动到另一个选项卡,停在任何地方。
澄清一下,这是在 Activity 中发生的,没有父片段。即使未显示片段,也会调用片段的 onViewCreated
。查看寻呼机似乎正确地恢复了 instantiateItem
中的片段引用。此外,在调试时,片段将 added
设置为 true,将 hidden
设置为 false。这让它看起来像是一个视图寻呼机渲染问题。
- 如何让视图寻呼机显示我的片段?
- 是否有更好的方法来实现我想要的行为?
试试这个
纵向Xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tab"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
横向Xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/fragmentA"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<FrameLayout
android:id="@+id/fragmentB"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
Activity Java:
package com.dev4solutions.myapplication;
import android.graphics.Point;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import com.dev4solutions.myapplication.fragment.FragmentA;
import com.dev4solutions.myapplication.fragment.FragmentB;
public class FragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Point point = new Point();
getWindowManager().getDefaultDisplay().getSize(point);
setContentView(R.layout.activity_frag);
if (point.y > point.x) {
TabLayout tabLayout = findViewById(R.id.tab);
ViewPager viewPager = findViewById(R.id.pager);
tabLayout.setupWithViewPager(viewPager);
viewPager.setAdapter(new PagerAdapter(getSupportFragmentManager()));
} else {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragmentA, new FragmentA())
.commit();
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragmentB, new FragmentB())
.commit();
}
}
}
您是否尝试过使用 PagerAdapter 而不是 FragmentPagerAdapter?您将更好地控制片段的创建和处理。否则你不知道 FragmentPagerAdapter 在做什么(他为你做的,但可能是错的)。
您可以将大部分逻辑转移到 class 中,使 activity 更清洁,并且可能还解决了您遇到的问题。
编辑:由于我们在这里讨论方向变化,您可能应该合并 onConfigurationChanged
回调以更好地处理该逻辑。
我找到的解决方案非常简单,像这样在我的寻呼机适配器中覆盖 getPageWidth
:
@Override
public float getPageWidth(int position) {
boolean hasTwoPanes = getResources().getBoolean(R.bool.hasTwoPanes);
return hasTwoPanes ? 0.5f : 1.0f;
}
R.bool.hasTwoPanes
是默认配置中可用的布尔资源,values-land
。两种配置我的布局相同,选项卡布局只是隐藏在横向上。
片段恢复由视图分页器自动完成。
我正在尝试实现一种布局,当设备纵向显示时显示一个视图寻呼机,当设备横向显示时显示两个窗格。
所以我做了两个不同的布局文件,一个只有一个ViewPager,一个有一个LinearLayout,一个有两个FrameLayout,我觉得没必要在这里展示。这两个配置还有一个布尔值hasTwoPanes
。
@Inject FragmentOne fragmentOne;
@Inject FragmentTwo fragmentTwo;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
boolean hasTwoPanes = getResources().getBoolean(R.bool.hasTwoPanes);
TabLayout tabLayout = findViewById(R.id.tab_layout);
ViewPager viewPager = findViewById(R.id.view_pager);
if (hasTwoPanes) {
tabLayout.setVisibility(View.GONE);
} else {
tabLayout.setVisibility(View.VISIBLE);
tabLayout.setupWithViewPager(viewPager);
viewPager.setAdapter(new MyPagerAdapter(fm));
}
FragmentOne frag1 = (FragmentOne) fm.findFragmentByTag(getFragmentName(0));
if (frag1 != null) fragmentOne = frag1;
FragmentTwo frag2 = (FragmentTwo) fm.findFragmentByTag(getFragmentName(1));
if (frag2 != null) fragmentTwo = frag2;
if (hasTwoPanes) {
if (frag1 != null) {
fm.beginTransaction().remove(fragmentOne).commit();
fm.beginTransaction().remove(fragmentTwo).commit();
fm.executePendingTransactions();
}
fm.beginTransaction().add(R.id.frame_frag1, fragmentOne, getFragmentName(0)).commit();
fm.beginTransaction().add(R.id.frame_frag2, fragmentTwo, getFragmentName(1)).commit();
}
}
private class MyPagerAdapter extends FragmentPagerAdapter {
MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
if (position == 0) {
return fragmentOne;
} else {
return fragmentTwo;
}
}
@Override
public int getCount() {
return 2;
}
}
private static String getFragmentName(int pos) {
return "android:switcher:" + R.id.view_pager + ":" + pos;
}
两个片段被注入了Dagger。如果不存在任何片段,这些注入的片段将根据方向添加到视图寻呼机或布局中。
因为视图寻呼机适配器为其片段命名,我需要知道该名称(因此 getFragmentName(int pos)
方法)才能在旋转后取回该片段。
结果是从纵向旋转到横向时状态正确恢复,但是当从横向旋转到纵向时,视图寻呼机完全是空的。当我旋转回横向时,碎片重新出现。选项卡布局也有问题,没有滑动动画,我只能从一个选项卡连续滑动到另一个选项卡,停在任何地方。
澄清一下,这是在 Activity 中发生的,没有父片段。即使未显示片段,也会调用片段的 onViewCreated
。查看寻呼机似乎正确地恢复了 instantiateItem
中的片段引用。此外,在调试时,片段将 added
设置为 true,将 hidden
设置为 false。这让它看起来像是一个视图寻呼机渲染问题。
- 如何让视图寻呼机显示我的片段?
- 是否有更好的方法来实现我想要的行为?
试试这个
纵向Xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tab"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
横向Xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/fragmentA"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<FrameLayout
android:id="@+id/fragmentB"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
Activity Java:
package com.dev4solutions.myapplication;
import android.graphics.Point;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import com.dev4solutions.myapplication.fragment.FragmentA;
import com.dev4solutions.myapplication.fragment.FragmentB;
public class FragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Point point = new Point();
getWindowManager().getDefaultDisplay().getSize(point);
setContentView(R.layout.activity_frag);
if (point.y > point.x) {
TabLayout tabLayout = findViewById(R.id.tab);
ViewPager viewPager = findViewById(R.id.pager);
tabLayout.setupWithViewPager(viewPager);
viewPager.setAdapter(new PagerAdapter(getSupportFragmentManager()));
} else {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragmentA, new FragmentA())
.commit();
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragmentB, new FragmentB())
.commit();
}
}
}
您是否尝试过使用 PagerAdapter 而不是 FragmentPagerAdapter?您将更好地控制片段的创建和处理。否则你不知道 FragmentPagerAdapter 在做什么(他为你做的,但可能是错的)。
您可以将大部分逻辑转移到 class 中,使 activity 更清洁,并且可能还解决了您遇到的问题。
编辑:由于我们在这里讨论方向变化,您可能应该合并 onConfigurationChanged
回调以更好地处理该逻辑。
我找到的解决方案非常简单,像这样在我的寻呼机适配器中覆盖 getPageWidth
:
@Override
public float getPageWidth(int position) {
boolean hasTwoPanes = getResources().getBoolean(R.bool.hasTwoPanes);
return hasTwoPanes ? 0.5f : 1.0f;
}
R.bool.hasTwoPanes
是默认配置中可用的布尔资源,values-land
。两种配置我的布局相同,选项卡布局只是隐藏在横向上。
片段恢复由视图分页器自动完成。