android 如何正确使用ViewPager
How to properly use ViewPager in android
我想制作一个测验应用程序。到目前为止,我有 3 个活动 - 家庭、测验、得分。由于测验 activity 包含多个等效视图(图像 header、问题和 4 个答案按钮),我做了一些阅读并决定
ViewPager
和 FragmentStatePagerAdapter
表演就可以了。所以我制作了一个 xml
模板并膨胀了几个测试视图,在我开始处理用户交互之前,一切看起来都很好。
我想模拟一个切换按钮,每个问题只有一个正确答案,所以 select按一个按钮应该取消 select 前一个(如果有的话)。当按下按钮时,我更改 Question model
,然后我找到所有 4 个带有 findViewById
的按钮并重置它们的滤色器。然后我将该过滤器重新设置到我的 selected 按钮上。为了确定要更新哪个问题模型,我使用了我在模板根视图中设置的当前 fragment
位置(使用 setTag
,在片段的 onCreate 中)。
我是这样称呼我的碎片的:
public Fragment getItem(int position) {
Question question = Repository.findById(position);
int correctAnswerBtnId;
switch (question.getCorrectAnswerIndex()) {
case 0: correctAnswerBtnId = R.id.quiz_answer_0_btn; break;
case 1: correctAnswerBtnId = R.id.quiz_answer_1_btn; break;
case 2: correctAnswerBtnId = R.id.quiz_answer_2_btn; break;
case 3: correctAnswerBtnId = R.id.quiz_answer_3_btn; break;
this.ACTIVITY_ROOT.setTag(question.getID());
Fragment fragment = new QuestionFragment();
Bundle args = new Bundle();
args.putSerializable(QuestionFragment.QUESTION, question);
fragment.setArguments(args);
return fragment;
}
我的 QuestionFragment onCreateView 与文档一致:
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle questionData) {
this.rootView = inflater.inflate(
R.layout.layout_question_template,
container,
false);
Bundle args = getArguments();
this.question = (Question) args.getSerializable(QuestionFragment.QUESTION);
populateInflatable();
rootView.findViewById(R.id.layout_question_template_root).setTag(this.question.getID());
return rootView;
}
在 populateInflatable
中,我使用 this.rootView
到 fintViewById
并用我的问题数据填充它。然后,如果问题中有 selected 按钮,我会更改按钮的颜色。
点击按钮我调用 selectAnserButton
:
public void selectAnswerButton(View selectedButton) {
int questionId =
(int) this.activityRoot.findViewById(
R.id.layout_question_template_root).getTag(); //??
unSelectAllButtons();
changeColor(selectedButton);
Repository.findById(questionId).selectAnswer(selectedButton.getId());
}
其中 unSelectAllButtons
代表四个按钮上的 buttonToUnSelect.getBackground().clearColorFilter();
。 Repository
只是一个带有示例问题数据的 static
class。
当我有不止一种观点时,一切都变得非常错误。在每个片段上,我用相同的视图 ID 膨胀相同的 xml
,正如我定义的那样。正如我现在所理解的那样,调用 findViewById
不会检索一个,而是会从我当前的视图中检索所有具有该 ID 的视图,还会从我的上一个和下一个 fragment
中检索。所以每次我想 select 我当前 fragment
的视图时,我也会修改上一个和下一个 fragments
中的相同视图。你可以想象这是多么有问题。这让我觉得我犯了一个根本性的错误,因为我认为不应该有超过一个 View
具有相同的 ID
.
我真的不明白我应该如何使用 ViewPager 来做到这一点。在这一点上,感觉就像我在尝试制作木雕,但我却在将框架砍成碎片。一定有更好的方法用 ViewPager 来做到这一点。
已解决:感谢 Soo Chun Jung 指出我的答案。简而言之,它对我有用的是:
- 使用
Bundle
将我的 Question model
id 传递给每个片段。
- 将每个片段存储在
ArrayMap
中,片段位置为 key
,片段为 value
。
- 从我的
selectAnswer
函数中获取每个单独的片段现在很容易:首先使用 myViewPager.getCurrentItem
获取当前片段的位置,然后调用 getter
函数 returns 一个片段在当前位置。
- 现在我有了片段,我可以轻松地更改它的按钮,因为它们被保存为私有字段,在“onCreateView”方法中分配。
希望对你有帮助~
适配器
class CustomAdapter extends FragmentPagerAdapter {
private final String[] TITLES = {"A", "B"};
private final String TAG = CustomAdapter.class.getSimpleName();
private final ArrayList<Fragment> mFragments;
private final FragmentManager fm;
public CustomAdapter(FragmentManager fm) {
super(fm);
mFragments = new ArrayList<>(getCount());
this.fm = fm;
}
@Override
public CharSequence getPageTitle(int position) {
return TITLES[position];
}
@Override
public int getCount() {
return TITLES.length;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d(TAG, "destroyItem position = " + position);
mFragments.remove(object);
super.destroyItem(container, position, object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object object = super.instantiateItem(container, position);
mFragments.add((Fragment) object);
return object;
}
@Override
public Fragment getItem(int position) {
Log.d(TAG, "getItem position = " + position);
if (position == 0) {
return MyFragmentA.newInstance();
} else if (position == 1) {
return MyFragmentB.newInstance();
}
return null;
}
public MyFragmentA getMyFragmentA() {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentA) {
return (MyFragmentA) f;
}
}
}
return null;
}
public MyFragmentB getMyFragmentB() {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentB) {
return (MyFragmentB) f;
}
}
}
return null;
}
}
片段class
public class MyFragmentB extends Fragment {
...
public updateYourUI(){
//update something
}
}
用法
mPager = (CustomViewPager) findViewById(R.id.pager);
mAdapter = new CustomAdapter(getChildFragmentManager());
mPager.setAdapter(mAdapter);
mAdapter.getMyFragmentB().updateYourUI();
如果您只有一种片段,请在下面发表评论。你可以像这样修改一些功能。
public static MyFragmentB newInstance(int ID) {
MyFragmentB fragment = new MyFragmentB();
Bundle bundle = new Bundle();
bundle.putInt("ID", ID);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
myID = getArguments().getInt("ID");
....
}
public int getMyID() {
return myID;
}
public MyFragmentB getMyFragmentByID(String id) {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentB) {
MyFragmentB temp = (MyFragmentB)f;
if(temp.getID.equals(id){
return (MyFragmentB) f;
}
}
}
}
return null;
}
我想制作一个测验应用程序。到目前为止,我有 3 个活动 - 家庭、测验、得分。由于测验 activity 包含多个等效视图(图像 header、问题和 4 个答案按钮),我做了一些阅读并决定
ViewPager
和 FragmentStatePagerAdapter
表演就可以了。所以我制作了一个 xml
模板并膨胀了几个测试视图,在我开始处理用户交互之前,一切看起来都很好。
我想模拟一个切换按钮,每个问题只有一个正确答案,所以 select按一个按钮应该取消 select 前一个(如果有的话)。当按下按钮时,我更改 Question model
,然后我找到所有 4 个带有 findViewById
的按钮并重置它们的滤色器。然后我将该过滤器重新设置到我的 selected 按钮上。为了确定要更新哪个问题模型,我使用了我在模板根视图中设置的当前 fragment
位置(使用 setTag
,在片段的 onCreate 中)。
我是这样称呼我的碎片的:
public Fragment getItem(int position) {
Question question = Repository.findById(position);
int correctAnswerBtnId;
switch (question.getCorrectAnswerIndex()) {
case 0: correctAnswerBtnId = R.id.quiz_answer_0_btn; break;
case 1: correctAnswerBtnId = R.id.quiz_answer_1_btn; break;
case 2: correctAnswerBtnId = R.id.quiz_answer_2_btn; break;
case 3: correctAnswerBtnId = R.id.quiz_answer_3_btn; break;
this.ACTIVITY_ROOT.setTag(question.getID());
Fragment fragment = new QuestionFragment();
Bundle args = new Bundle();
args.putSerializable(QuestionFragment.QUESTION, question);
fragment.setArguments(args);
return fragment;
}
我的 QuestionFragment onCreateView 与文档一致:
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle questionData) {
this.rootView = inflater.inflate(
R.layout.layout_question_template,
container,
false);
Bundle args = getArguments();
this.question = (Question) args.getSerializable(QuestionFragment.QUESTION);
populateInflatable();
rootView.findViewById(R.id.layout_question_template_root).setTag(this.question.getID());
return rootView;
}
在 populateInflatable
中,我使用 this.rootView
到 fintViewById
并用我的问题数据填充它。然后,如果问题中有 selected 按钮,我会更改按钮的颜色。
点击按钮我调用 selectAnserButton
:
public void selectAnswerButton(View selectedButton) {
int questionId =
(int) this.activityRoot.findViewById(
R.id.layout_question_template_root).getTag(); //??
unSelectAllButtons();
changeColor(selectedButton);
Repository.findById(questionId).selectAnswer(selectedButton.getId());
}
其中 unSelectAllButtons
代表四个按钮上的 buttonToUnSelect.getBackground().clearColorFilter();
。 Repository
只是一个带有示例问题数据的 static
class。
当我有不止一种观点时,一切都变得非常错误。在每个片段上,我用相同的视图 ID 膨胀相同的 xml
,正如我定义的那样。正如我现在所理解的那样,调用 findViewById
不会检索一个,而是会从我当前的视图中检索所有具有该 ID 的视图,还会从我的上一个和下一个 fragment
中检索。所以每次我想 select 我当前 fragment
的视图时,我也会修改上一个和下一个 fragments
中的相同视图。你可以想象这是多么有问题。这让我觉得我犯了一个根本性的错误,因为我认为不应该有超过一个 View
具有相同的 ID
.
我真的不明白我应该如何使用 ViewPager 来做到这一点。在这一点上,感觉就像我在尝试制作木雕,但我却在将框架砍成碎片。一定有更好的方法用 ViewPager 来做到这一点。
已解决:感谢 Soo Chun Jung 指出我的答案。简而言之,它对我有用的是:
- 使用
Bundle
将我的Question model
id 传递给每个片段。 - 将每个片段存储在
ArrayMap
中,片段位置为key
,片段为value
。 - 从我的
selectAnswer
函数中获取每个单独的片段现在很容易:首先使用myViewPager.getCurrentItem
获取当前片段的位置,然后调用getter
函数 returns 一个片段在当前位置。 - 现在我有了片段,我可以轻松地更改它的按钮,因为它们被保存为私有字段,在“onCreateView”方法中分配。
希望对你有帮助~
适配器
class CustomAdapter extends FragmentPagerAdapter {
private final String[] TITLES = {"A", "B"};
private final String TAG = CustomAdapter.class.getSimpleName();
private final ArrayList<Fragment> mFragments;
private final FragmentManager fm;
public CustomAdapter(FragmentManager fm) {
super(fm);
mFragments = new ArrayList<>(getCount());
this.fm = fm;
}
@Override
public CharSequence getPageTitle(int position) {
return TITLES[position];
}
@Override
public int getCount() {
return TITLES.length;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d(TAG, "destroyItem position = " + position);
mFragments.remove(object);
super.destroyItem(container, position, object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object object = super.instantiateItem(container, position);
mFragments.add((Fragment) object);
return object;
}
@Override
public Fragment getItem(int position) {
Log.d(TAG, "getItem position = " + position);
if (position == 0) {
return MyFragmentA.newInstance();
} else if (position == 1) {
return MyFragmentB.newInstance();
}
return null;
}
public MyFragmentA getMyFragmentA() {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentA) {
return (MyFragmentA) f;
}
}
}
return null;
}
public MyFragmentB getMyFragmentB() {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentB) {
return (MyFragmentB) f;
}
}
}
return null;
}
}
片段class
public class MyFragmentB extends Fragment {
...
public updateYourUI(){
//update something
}
}
用法
mPager = (CustomViewPager) findViewById(R.id.pager);
mAdapter = new CustomAdapter(getChildFragmentManager());
mPager.setAdapter(mAdapter);
mAdapter.getMyFragmentB().updateYourUI();
如果您只有一种片段,请在下面发表评论。你可以像这样修改一些功能。
public static MyFragmentB newInstance(int ID) {
MyFragmentB fragment = new MyFragmentB();
Bundle bundle = new Bundle();
bundle.putInt("ID", ID);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
myID = getArguments().getInt("ID");
....
}
public int getMyID() {
return myID;
}
public MyFragmentB getMyFragmentByID(String id) {
synchronized (mFragments) {
for (Fragment f : mFragments) {
if (f instanceof MyFragmentB) {
MyFragmentB temp = (MyFragmentB)f;
if(temp.getID.equals(id){
return (MyFragmentB) f;
}
}
}
}
return null;
}