我正在使用兼容性库中的ViewPager。我已经巧妙地让它显示了几个我可以翻阅的视图。
但是,我很难弄清楚如何使用一组新视图更新ViewPager。
我尝试了各种各样的事情,比如每次我想使用新的数据列表时调用mAdapter.notifyDataSetChanged()
,mViewPager.invalidate()
甚至创建一个全新的适配器。
没有任何帮助,文本视图与原始数据保持不变。
更新 我做了一个小测试项目,我几乎能够更新视图。我将粘贴下面的课程。
似乎没有更新的是第二个视图,'B'仍然存在,按下更新按钮后它应显示'Y'。
public class ViewPagerBugActivity extends Activity {
private ViewPager myViewPager;
private List<String> data;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
data = new ArrayList<String>();
data.add("A");
data.add("B");
data.add("C");
myViewPager = (ViewPager) findViewById(R.id.my_view_pager);
myViewPager.setAdapter(new MyViewPagerAdapter(this, data));
Button updateButton = (Button) findViewById(R.id.update_button);
updateButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateViewPager();
}
});
}
private void updateViewPager() {
data.clear();
data.add("X");
data.add("Y");
data.add("Z");
myViewPager.getAdapter().notifyDataSetChanged();
}
private class MyViewPagerAdapter extends PagerAdapter {
private List<String> data;
private Context ctx;
public MyViewPagerAdapter(Context ctx, List<String> data) {
this.ctx = ctx;
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object instantiateItem(View collection, int position) {
TextView view = new TextView(ctx);
view.setText(data.get(position));
((ViewPager)collection).addView(view);
return view;
}
@Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
@Override
public void startUpdate(View arg0) {
}
@Override
public void finishUpdate(View arg0) {
}
}
}
答案 0 :(得分:795)
有几种方法可以实现这一目标。
第一种选择更容易,但效率更低。
覆盖getItemPosition
中的PagerAdapter
,如下所示:
public int getItemPosition(Object object) {
return POSITION_NONE;
}
这样,当您调用notifyDataSetChanged()
时,视图寻呼机将删除所有视图并重新加载它们。因此,获得了重载效果。
第二个选项suggested by Alvaro Luis Bustamante (previously alvarolb)是实例化新视图时setTag()
中的instantiateItem()
方法。然后,您可以使用notifyDataSetChanged()
查找要更新的视图,而不是使用findViewWithTag()
。
第二种方法非常灵活,性能高。感谢alvarolb的原创研究。
答案 1 :(得分:464)
我认为PagerAdapter
中没有任何类型的错误。问题是了解它的工作原理有点复杂。看看这里解释的解决方案,从我的角度来看,存在一个误解,因此对实例化视图的使用很差。
过去几天我一直在使用PagerAdapter
和ViewPager
,我发现了以下内容:
notifyDataSetChanged()
上的PagerAdapter
方法只会通知ViewPager
基础页面已更改。例如,如果您已动态创建/删除页面(在列表中添加或删除项目),则ViewPager
应该处理此问题。在这种情况下,我认为ViewPager
确定是否应使用getItemPosition()
和getCount()
方法删除或实例化新视图。
我认为在ViewPager
调用后,notifyDataSetChanged()
会获取子视图,并使用getItemPosition()
检查其位置。如果对于子视图,此方法返回POSITION_NONE
,则ViewPager
会理解视图已被删除,调用destroyItem()
并删除此视图。
通过这种方式,如果您只想更新页面的内容,则覆盖getItemPosition()
以始终返回POSITION_NONE
是完全错误的,因为先前创建的视图将被销毁并且将创建新的视图每次拨打notifyDatasetChanged()
时。对于少数TextView
来说,似乎并没有错,但是当你有复杂的视图时,比如从数据库填充的ListViews,这可能是一个真正的问题而且浪费资源。
因此,有几种方法可以有效地更改视图的内容,而无需再次删除和实例化视图。这取决于您要解决的问题。 我的方法是对setTag()
方法中的任何实例化视图使用instantiateItem()
方法。因此,当您想要更改数据或使您需要的视图无效时,可以调用findViewWithTag()
上的ViewPager
方法来检索以前实例化的视图,并根据需要修改/使用它,而不必每次要更新某个值时删除/创建新视图。
想象一下,例如,您有100页{100} TextView
,您只想定期更新一个值。使用前面介绍的方法,这意味着您要在每次更新时删除并实例化100 TextView
s。这没有意义......
答案 2 :(得分:77)
将FragmentPagerAdapter
更改为FragmentStatePagerAdapter
。
覆盖getItemPosition()
方法并返回POSITION_NONE
。
最终,它会在视图寻呼机上收听notifyDataSetChanged()
。
答案 3 :(得分:43)
The answer绝对是最好的方法。基于他的回答,实现这一点的简单方法是按位置简单地存储活动视图:
SparseArray<View> views = new SparseArray<View>();
@Override
public Object instantiateItem(View container, int position) {
View root = <build your view here>;
((ViewPager) container).addView(root);
views.put(position, root);
return root;
}
@Override
public void destroyItem(View collection, int position, Object o) {
View view = (View)o;
((ViewPager) collection).removeView(view);
views.remove(position);
view = null;
}
然后,通过覆盖notifyDataSetChanged
方法,您可以刷新视图...
@Override
public void notifyDataSetChanged() {
int key = 0;
for(int i = 0; i < views.size(); i++) {
key = views.keyAt(i);
View view = views.get(key);
<refresh view with new data>
}
super.notifyDataSetChanged();
}
您实际上可以在instantiateItem
和notifyDataSetChanged
中使用类似的代码来刷新您的观点。在我的代码中,我使用完全相同的方法。
答案 4 :(得分:25)
有同样的问题。对我来说,它扩展了FragmentStatePagerAdapter,并覆盖了以下方法:
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
答案 5 :(得分:20)
在尝试上述所有解决方案以解决此问题并在其他类似问题(如this,this和this上尝试了许多解决方案时,经过几个小时的挫折,这些问题都与我失败了解决此问题并使ViewPager
销毁旧版Fragment
并使用新pager
填充Fragment
。我已经解决了以下问题:
1)让ViewPager
课程扩展FragmentPagerAdapter
,如下所示:
public class myPagerAdapter extends FragmentPagerAdapter {
2)为存储ViewPager
和title
的{{1}}创建一个项目,如下所示:
fragment
3)让public class PagerItem {
private String mTitle;
private Fragment mFragment;
public PagerItem(String mTitle, Fragment mFragment) {
this.mTitle = mTitle;
this.mFragment = mFragment;
}
public String getTitle() {
return mTitle;
}
public Fragment getFragment() {
return mFragment;
}
public void setTitle(String mTitle) {
this.mTitle = mTitle;
}
public void setFragment(Fragment mFragment) {
this.mFragment = mFragment;
}
}
的构造函数将我的ViewPager
实例存储在我的FragmentManager
中,如下所示:
class
4)创建一种方法,通过将private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;
public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
super(fragmentManager);
mFragmentManager = fragmentManager;
mPagerItems = pagerItems;
}
本身的所有先前adapter
直接删除到新数据来重新设置fragment
数据让fragmentManager
再次从新列表中设置新的adapter
,如下所示:
fragment
5)从容器public void setPagerItems(ArrayList<PagerItem> pagerItems) {
if (mPagerItems != null)
for (int i = 0; i < mPagerItems.size(); i++) {
mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
}
mPagerItems = pagerItems;
}
或Activity
不要使用新数据重新初始化适配器。使用以下新数据通过方法Fragment
设置新数据:
setPagerItems
我希望它有所帮助。
答案 6 :(得分:9)
我遇到了同样的问题,我的解决方案覆盖了ViewPagerAdapter#getItemId(int position)
:
@Override
public long getItemId(int position) {
return mPages.get(position).getId();
}
默认情况下,此方法返回项目的位置。我认为ViewPager
检查itemId
是否已更改,并且仅在重新创建页面时才会显示。但是,not-overriden版本返回与itemId
相同的位置,即使页面实际上不同,并且ViewPager也没有定义该页面被替换为一个并且需要重新创建。
要使用此功能,每个页面都需要long id
。通常情况下,它应该是唯一的,但我建议,对于这种情况,它应该与同一页面的先前值不同。因此,可以在适配器或随机整数(具有广泛分布)中使用连续计数器。
我认为这是更一致的方式,而不是使用本主题中提到的视图标签。但可能不适用于所有情况。
答案 7 :(得分:5)
感谢rui.araujo和Alvaro Luis Bustamante。起初,我尝试使用rui.araujo的方式,因为它很容易。它可以工作,但是当数据发生变化时,页面会重新显示。这很糟糕所以我尝试使用Alvaro Luis Bustamante的方式。这是完美的。这是代码:
@Override
protected void onStart() {
super.onStart();
}
private class TabPagerAdapter extends PagerAdapter {
@Override
public int getCount() {
return 4;
}
@Override
public boolean isViewFromObject(final View view, final Object object) {
return view.equals(object);
}
@Override
public void destroyItem(final View container, final int position, final Object object) {
((ViewPager) container).removeView((View) object);
}
@Override
public Object instantiateItem(final ViewGroup container, final int position) {
final View view = LayoutInflater.from(
getBaseContext()).inflate(R.layout.activity_approval, null, false);
container.addView(view);
ListView listView = (ListView) view.findViewById(R.id.list_view);
view.setTag(position);
new ShowContentListTask(listView, position).execute();
return view;
}
}
当数据发生变化时:
for (int i = 0; i < 4; i++) {
View view = contentViewPager.findViewWithTag(i);
if (view != null) {
ListView listView = (ListView) view.findViewById(R.id.list_view);
new ShowContentListTask(listView, i).execute();
}
}
答案 8 :(得分:5)
我发现这个问题很有意思。 我们可以使用 FragmentStatePagerAdapter ( android.support.v4.app.FragmentStatePagerAdapter ,而不是使用 FragmentPagerAdapter 来保存内存中的所有片段。 ),每当我们选择它时重新加载片段。
两个适配器的实现是相同的。所以,我们只需要在“扩展FragmentStatePagerAdapter ”上更改“扩展FragmentPagerAdapter ”
答案 9 :(得分:5)
更简单的方法:使用FragmentPagerAdapter
,并将分页视图包装到片段上。他们确实得到了更新
答案 10 :(得分:5)
所有这些解决方案都没有帮助我。因此我找到了一个有效的解决方
您每次都可以setAdapter
,但这还不够。
你应该在更改适配器之前执行这些操作:
FragmentManager fragmentManager = slideShowPagerAdapter.getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
List<Fragment> fragments = fragmentManager.getFragments();
for (Fragment f : fragments) {
transaction.remove(f);
}
transaction.commit();
之后:
viewPager.setAdapter(adapter);
答案 11 :(得分:5)
在我的片段(页面)中:
在我的活动中,我在这里加载页面:
在此之后,当您重新加载第二组页面时,该错误仍会导致一些显示旧数据。但是,它们现在将被刷新,您将看到新数据 - 您的用户将不会知道该页面是不正确的,因为这种刷新将在他们看到页面之前发生。
希望这有助于某人!
答案 12 :(得分:4)
如果有人使用基于FragmentStatePagerAdapter的适配器(这会让ViewPager创建显示目的所需的最小页面,对我的情况最多只有2个),@ rui.araujo在你的适配器中覆盖getItemPosition的答案不会造成重大浪费,但仍有待改进。
在伪代码中:
public int getItemPosition(Object object) {
YourFragment f = (YourFragment) object;
YourData d = f.data;
logger.info("validate item position on page index: " + d.pageNo);
int dataObjIdx = this.dataPages.indexOf(d);
if (dataObjIdx < 0 || dataObjIdx != d.pageNo) {
logger.info("data changed, discard this fragment.");
return POSITION_NONE;
}
return POSITION_UNCHANGED;
}
答案 13 :(得分:3)
我只是发布这个答案,以防其他人发现它有用。为了做同样的事情,我只是从兼容性库中获取了ViewPager和PagerAdapter的源代码,并在我的代码中编译它(你需要自己解决所有错误和导入,但绝对可以完成)。 / p>
然后,在CustomViewPager中,创建一个名为updateViewAt(int position)的方法。视图本身可以从ViewPager类中定义的ArrayList mItems获取(您需要为实例化项目中的视图设置Id,并将此id与updateViewAt()方法中的位置进行比较)。然后,您可以根据需要更新视图。
答案 14 :(得分:3)
在经过大量搜索之后,我发现了一个非常好的解决方案,我认为这是解决这个问题的正确方法。本质上,instantiateItem仅在实例化视图时被调用,除非视图被销毁,否则永远不会被调用(这是当您重写getItemPosition函数以返回POSITION_NONE时发生的情况)。相反,你想要做的是保存创建的视图,并在适配器中更新它们,生成一个get函数,以便其他人可以更新它,或者更新适配器的set函数(我最喜欢的)
因此,在MyViewPagerAdapter中添加一个变量,如:
private View updatableView;
在你的instantiateItem中:
public Object instantiateItem(View collection, int position) {
updatableView = new TextView(ctx); //My change is here
view.setText(data.get(position));
((ViewPager)collection).addView(view);
return view;
}
因此,您可以创建一个更新视图的函数:
public updateText(String txt)
{
((TextView)updatableView).setText(txt);
}
希望这有帮助!
答案 15 :(得分:3)
我有一个类似的问题,其中我有四个页面,其中一个页面更新了其他三个视图。我能够在当前页面旁边的页面上更新小部件(SeekBars,TextViews等)。调用mTabsAdapter.getItem(position)
时,最后两页将包含未初始化的小部件。
要解决我的问题,我在致电setSelectedPage(index)
之前使用了getItem(position)
。这将实例化页面,使我能够在每个页面上更改值和小部件。
完成所有更新后,我会使用setSelectedPage(position)
,然后使用notifyDataSetChanged()
。
您可以在主更新页面上的ListView中看到轻微的闪烁,但没有什么值得注意的。我没有彻底测试它,但它确实解决了我的直接问题。
答案 16 :(得分:1)
您可以在Viewpager上添加寻呼机转换,如下所示
myPager.setPageTransformer(true, new MapPagerTransform());
在下面的代码中,当寻呼机滚动
时,我在运行时更改了视图颜色public class MapPagerTransform implements ViewPager.PageTransformer {
public void transformPage(View view, float position) {
LinearLayout showSelectionLL=(LinearLayout)view.findViewById(R.id.showSelectionLL);
if (position < 0) {
showSelectionLL.setBackgroundColor(Color.WHITE);
}else if (position >0){
showSelectionLL.setBackgroundColor(Color.WHITE);
}else {
showSelectionLL.setBackgroundColor(Color.RED);
}
}
}
答案 17 :(得分:1)
以下代码对我有用。
创建一个扩展FragmentPagerAdapter类的类,如下所示。
public class Adapter extends FragmentPagerAdapter {
private int tabCount;
private Activity mActivity;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
private int container_id;
private ViewGroup container;
private List<Object> object;
public Adapter(FragmentManager fm) {
super(fm);
}
public Adapter(FragmentManager fm, int numberOfTabs , Activity mA) {
super(fm);
mActivity = mA;
mFragmentManager = fm;
object = new ArrayList<>();
mFragmentTags = new HashMap<Integer, String>();
this.tabCount = numberOfTabs;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return Fragment0.newInstance(mActivity);
case 1:
return Fragment1.newInstance(mActivity);
case 2:
return Fragment2.newInstance(mActivity);
default:
return null;
}}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object object = super.instantiateItem(container, position);
if (object instanceof Fragment) {
Log.e("Already defined","Yes");
Fragment fragment = (Fragment) object;
String tag = fragment.getTag();
Log.e("Fragment Tag","" + position + ", " + tag);
mFragmentTags.put(position, tag);
}else{
Log.e("Already defined","No");
}
container_id = container.getId();
this.container = container;
if(position == 0){
this.object.add(0,object);
}else if(position == 1){
this.object.add(1,object);
}else if(position == 2){
this.object.add(2,object);
}
return object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
if (object instanceof Fragment) {
Log.e("Removed" , String.valueOf(position));
}
}
@Override
public int getItemPosition (Object object)
{ int index = 0;
if(this.object.get(0) == object){
index = 0;
}else if(this.object.get(1) == object){
index = 1;
}else if(this.object.get(2) == object){
index = 2;
}else{
index = -1;
}
Log.e("Index" , "..................." + String.valueOf(index));
if (index == -1)
return POSITION_NONE;
else
return index;
}
public String getFragmentTag(int pos){
return "android:switcher:"+R.id.pager+":"+pos;
}
public void NotifyDataChange(){
this.notifyDataSetChanged();
}
public int getcontainerId(){
return container_id;
}
public ViewGroup getContainer(){
return this.container;
}
public List<Object> getObject(){
return this.object;
}
@Override
public int getCount() {
return tabCount;
}}
然后在您创建的每个Fragment中,创建一个updateFragment方法。在此方法中,您可以更改片段中需要更改的内容。例如,在我的例子中,Fragment0包含一个GLSurfaceView,它根据.ply文件的路径显示一个3d对象,所以在我的updateFragment方法中,我改变了这个ply文件的路径。
然后创建一个ViewPager实例
viewPager = (ViewPager) findViewById(R.id.pager);
和一个Adpater实例,
adapter = new Adapter(getSupportFragmentManager(), 3, this);
然后这样做,
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(1);
然后在类中你初始化了上面的Adapter类并创建了一个viewPager,每次你想要更新你的一个片段(在我们的例子中是Fragment0)时都使用以下内容:
adapter.NotifyDataChange();
adapter.destroyItem(adapter.getContainer(), 0, adapter.getObject().get(0)); // destroys page 0 in the viewPager.
fragment0 = (Fragment0) getSupportFragmentManager().findFragmentByTag(adapter.getFragmentTag(0)); // Gets fragment instance used on page 0.
fragment0.updateFragment() method which include the updates on this fragment
adapter.instantiateItem(adapter.getContainer(), 0); // re-initialize page 0.
此解决方案基于Alvaro Luis Bustamante建议的技术。
答案 18 :(得分:1)
我知道我要迟到,但它仍然可以帮助某人。我只是扩展了正确的答案,而且还添加了对此的评论。
好吧
答案本身表示效率低下
因此,为了使其仅在需要时刷新,您可以这样做
private boolean refresh;
public void refreshAdapter(){
refresh = true;
notifyDataSetChanged();
}
@Override
public int getItemPosition(@NonNull Object object) {
if(refresh){
refresh = false;
return POSITION_NONE;
}else{
return super.getItemPosition(object);
}
}
答案 19 :(得分:1)
这适用于所有像我这样的人,需要从服务(或其他后台线程)更新Viewpager,并且没有任何提议有效: 经过一些logchecking我意识到,notifyDataSetChanged()方法永远不会返回。 getItemPosition(Object object)被称为all ends,无需进一步处理。然后我在父PagerAdapter类的文档中找到(不在子类的文档中),&#34;数据集更改必须在主线程上发生,并且必须以对notifyDataSetChanged()&#34;的调用结束。 因此,在这种情况下,工作解决方案是(使用FragmentStatePagerAdapter 和getItemPosition(Object object)设置为返回POSITION_NONE):
然后调用notifyDataSetChanged():
runOnUiThread(new Runnable() {
@Override
public void run() {
pager.getAdapter().notifyDataSetChanged();
}
});
答案 20 :(得分:1)
我想,我已经掌握了ViewPager的逻辑。
如果我需要刷新一组页面并根据新数据集显示它们,我会调用 notifyDataSetChanged()。 然后,ViewPager对 getItemPosition()进行了多次调用,并将Fragment作为Object传递给它。此片段可以来自旧数据集(我想要丢弃),也可以来自新数据集(我想要显示)。所以,我重写 getItemPosition(),我必须以某种方式确定我的片段是来自旧数据集还是来自新数据集。
在我的情况下,我有一个双窗格布局,左侧窗格中有顶部项目列表,右侧是滑动视图(ViewPager)。所以,我在我的PagerAdapter中存储了一个链接到我当前的顶级项目,并且还存储了每个实例化页面片段内的链接。 当列表中选定的顶级项目发生更改时,我将新的顶级项目存储在PagerAdapter中并调用 notifyDataSetChanged()。在重写的 getItemPosition()中,我将我的适配器中的顶级项目与我的片段中的顶级项目进行比较。 只有当它们不相等时,我才会返回POSITION_NONE。 然后,PagerAdapter重新实例化已返回POSITION_NONE的所有片段。
注意。存储热门商品ID而不是参考可能是个更好的主意。
下面的代码片段有点示意,但我从实际工作的代码中对其进行了调整。
public class SomeFragment extends Fragment {
private TopItem topItem;
}
public class SomePagerAdapter extends FragmentStatePagerAdapter {
private TopItem topItem;
public void changeTopItem(TopItem newTopItem) {
topItem = newTopItem;
notifyDataSetChanged();
}
@Override
public int getItemPosition(Object object) {
if (((SomeFragment) object).getTopItemId() != topItem.getId()) {
return POSITION_NONE;
}
return super.getItemPosition(object);
}
}
感谢所有以前的研究人员!
答案 21 :(得分:1)
ViewPager并非旨在支持动态视图更改。
我在寻找与此https://issuetracker.google.com/issues/36956111,尤其是https://issuetracker.google.com/issues/36956111#comment56
相关的另一个错误时得到了确认。这个问题有点老了,但是Google最近用ViewPager2解决了这个问题。 它将允许用标准解决方案替代手工解决方案(未维护且可能有故障的解决方案)。它还可以防止不必要地重新创建视图,就像某些答案一样。
对于ViewPager2示例,您可以检查https://github.com/googlesamples/android-viewpager2
如果要使用ViewPager2,则需要在build.gradle文件中添加以下依赖项:
dependencies {
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
}
然后,您可以将xml文件中的ViewPager替换为:
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
在那之后,您将需要在活动中将ViewPager替换为ViewPager2
ViewPager2需要RecyclerView.Adapter或FragmentStateAdapter,在您的情况下可以是RecyclerView.Adapter
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private ArrayList<String> arrayList = new ArrayList<>();
public MyAdapter(Context context, ArrayList<String> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.tvName.setText(arrayList.get(position));
}
@Override
public int getItemCount() {
return arrayList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView tvName;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
tvName = itemView.findViewById(R.id.tvName);
}
}
}
如果使用的是TabLayout,则可以使用TabLayoutMediator:
TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, true, new TabLayoutMediator.OnConfigureTabCallback() {
@Override
public void onConfigureTab(@NotNull TabLayout.Tab tab, int position) {
// configure your tab here
tab.setText(tabs.get(position).getTitle());
}
});
tabLayoutMediator.attach();
然后,您将可以通过修改适配器的数据并调用notifyDataSetChanged方法来刷新视图
答案 22 :(得分:1)
始终返回POSITION_NONE
很简单,但效率有点低,因为这会唤起已经实例化的所有页面的实例化。
我创建了一个library ArrayPagerAdapter来动态更改PagerAdapters中的项目。
在内部,只有在必要时,此库的适配器才会在POSITION_NONE
上返回getItemPosiition()
。
您可以使用此库动态更改项目。
@Override
protected void onCreate(Bundle savedInstanceState) {
/** ... **/
adapter = new MyStatePagerAdapter(getSupportFragmentManager()
, new String[]{"1", "2", "3"});
((ViewPager)findViewById(R.id.view_pager)).setAdapter(adapter);
adapter.add("4");
adapter.remove(0);
}
class MyPagerAdapter extends ArrayViewPagerAdapter<String> {
public MyPagerAdapter(String[] data) {
super(data);
}
@Override
public View getView(LayoutInflater inflater, ViewGroup container, String item, int position) {
View v = inflater.inflate(R.layout.item_page, container, false);
((TextView) v.findViewById(R.id.item_txt)).setText(item);
return v;
}
}
Thils库还支持Fragments创建的页面。
答案 23 :(得分:1)
您可以动态更新所有片段,您可以分三步查看。
在适配器中:
public class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
public MyPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
mFragmentManager = fragmentManager;
mFragmentTags = new HashMap<Integer, String>();
}
// Returns total number of pages
@Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for that page
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return FirstFragment.newInstance();
case 1:
return SecondFragment.newInstance();
case 2:
return ThirdFragment.newInstance();
default:
return null;
}
}
// Returns the page title for the top indicator
@Override
public CharSequence getPageTitle(int position) {
return "Page " + position;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object object = super.instantiateItem(container, position);
if (object instanceof Fragment) {
Fragment fragment = (Fragment) object;
String tag = fragment.getTag();
mFragmentTags.put(position, tag);
}
return object;
}
public Fragment getFragment(int position) {
Fragment fragment = null;
String tag = mFragmentTags.get(position);
if (tag != null) {
fragment = mFragmentManager.findFragmentByTag(tag);
}
return fragment;
}}
现在进入您的活动:
public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener{
MyPagerAdapter mAdapterViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager viewPager = (ViewPager) findViewById(R.id.vpPager);
mAdapterViewPager = new MyPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(mAdapterViewPager);
viewPager.addOnPageChangeListener(this);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
Fragment fragment = mAdapterViewPager.getFragment(position);
if (fragment != null) {
fragment.onResume();
}
}
@Override
public void onPageScrollStateChanged(int state) {
}}
最后在你的片段中,类似的东西:
public class YourFragment extends Fragment {
// newInstance constructor for creating fragment with arguments
public static YourFragment newInstance() {
return new YourFragment();
}
// Store instance variables based on arguments passed
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
// Inflate the view for the fragment based on layout XML
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment, container, false);
}
@Override
public void onResume() {
super.onResume();
//to refresh your view
refresh();
}}
您可以看到完整的代码here。
感谢Alvaro Luis Bustamante。
答案 24 :(得分:1)
对我有用的是viewPager.getAdapter().notifyDataSetChanged();
并在适配器中放置代码以更新getItemPosition
内的视图,如此
@Override
public int getItemPosition(Object object) {
if (object instanceof YourViewInViewPagerClass) {
YourViewInViewPagerClass view = (YourViewInViewPagerClass)object;
view.setData(data);
}
return super.getItemPosition(object);
}
可能不是最正确的方法,但它有效(return POSITION_NONE
技巧导致我崩溃所以不是一个选项)
答案 25 :(得分:0)
1.首先,您必须在Pageradapter类中设置getItemposition方法 2.您必须阅读View Pager的确切位置 3.然后将该位置作为新位置的数据位置发送 4.在setonPageChange监听器
中写入更新按钮onclick监听器程序代码有点修改,只能设置特定的位置元素
public class MyActivity extends Activity {
private ViewPager myViewPager;
private List<String> data;
public int location=0;
public Button updateButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
data = new ArrayList<String>();
data.add("A");
data.add("B");
data.add("C");
data.add("D");
data.add("E");
data.add("F");
myViewPager = (ViewPager) findViewById(R.id.pager);
myViewPager.setAdapter(new MyViewPagerAdapter(this, data));
updateButton = (Button) findViewById(R.id.update);
myViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i2) {
//Toast.makeText(MyActivity.this, i+" Is Selected "+data.size(), Toast.LENGTH_SHORT).show();
}
@Override
public void onPageSelected( int i) {
// here you will get the position of selected page
final int k = i;
updateViewPager(k);
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
}
private void updateViewPager(final int i) {
updateButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MyActivity.this, i+" Is Selected "+data.size(), Toast.LENGTH_SHORT).show();
data.set(i, "Replaced "+i);
myViewPager.getAdapter().notifyDataSetChanged();
}
});
}
private class MyViewPagerAdapter extends PagerAdapter {
private List<String> data;
private Context ctx;
public MyViewPagerAdapter(Context ctx, List<String> data) {
this.ctx = ctx;
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
@Override
public Object instantiateItem(View collection, int position) {
TextView view = new TextView(ctx);
view.setText(data.get(position));
((ViewPager)collection).addView(view);
return view;
}
@Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
@Override
public void startUpdate(View arg0) {
}
@Override
public void finishUpdate(View arg0) {
}
}
}
答案 26 :(得分:0)
在 ViewPager2 中,您可以再次重新初始化适配器以使用新视图刷新寻呼机列表。 viewPager2.adapter = myPagerAdapter
答案 27 :(得分:0)
在我的情况下,我的Viewpager中有一个textView,在mainActivity上单击一个按钮,我想更改该textView的颜色并更新pagerAdapter。在按钮上,单击我将颜色保存在SharedPreference中并更新pagerAdapter,它可以更新从共享首选项中获取的颜色。因此,我通过以下方式更新viewPager视图。
btn_purple.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int color = ContextCompat.getColor(mContext, R.color.colorPrimaryDark2);
editor.putInt("sahittoFontColor", color);
editor.apply();
toNotifyDatasetChanged();
}
});
现在的更新方法:
private void toNotifyDatasetChanged (){
if(viewPager!=null&& pagerAdapter!=null) {
viewPager.setAdapter(null);
viewPager.setAdapter(pagerAdapter);
}
}
我的pagerAdapter是:
pagerAdapter = new Sahitto_ViewPagerAdapter (mContext, filenameParameter, 30, lineList);
viewPager.setAdapter(pagerAdapter);
在InstantiateItem中是(在PagerAdapter中):
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mContext);
int bnfntcolor=settings.getInt("sahittoFontColor", 0);
if (bnfntcolor!=0){
textView.setTextColor(bnfntcolor);
}
因此,当我单击按钮时,颜色会立即在pagerAdapter的Textview中更改。
快乐的编码。
答案 28 :(得分:0)
所有这些答案都不适合我。
对我唯一有用的是我必须再次将适配器设置为viewpager,然后它将刷新内容。
我的PagerAdaper中的removeView(int pos)
public void removeView(int index) {
imageFileNames.remove(index);
notifyDataSetChanged();
}
无论我在哪里删除文件,我都必须这样做
imagePagerAdapter.removeView(currentPosition);
viewPager.setAdapter(imagePagerAdapter);
编辑:
以下方法有效,您可以应用以下方法。
public void updateView(int pos){
viewPager.setAdapter(null);
imagePagerAdapter =new ImagePagerAdapter(YOUR_CONTEXT,YOUR_CONTENT);
viewPager.setAdapter(imagePagerAdapter);
viewPager.setCurrentItem(pos);
}
将YOUR_CONTEXT替换为上下文,将内容替换为内容名称,即更新列表或其他内容。
答案 29 :(得分:0)
我实际上在notifyDataSetChanged()
和ViewPager
上使用了CirclePageIndicator
,之后我在destroyDrawingCache()
上调用ViewPager
并且它正常工作..其他解决方案都没有工作对我来说。
答案 30 :(得分:0)
根据我的经验,最好的解决方案是https://stackoverflow.com/a/44177688/3118950,它是覆盖long getItemId()
并返回唯一ID而不是默认位置的方法。除了该答案外,还会导入旧的片段,以防总数量小于页面限制并且在片段为onDetach()
/ onDestory()
时不会调用旧片段更换。
答案 31 :(得分:0)
您只需要以下简单代码:
代码:
private void updateAdapter(){
if(adapterViewPager != null){
int from = vpMyViewPager.getCurrentItem() - vpMyViewPager.getOffscreenPageLimit();
int to = vpMyViewPager.getCurrentItem() + vpMyViewPager.getOffscreenPageLimit();
vpMyViewPager.removeAllViews();
for(int i = from; i <= to; i++){
if(i < 0){
continue;
}
adapterViewPager.instantiateItem(vpMyViewPager, i);
}
}
}
说明:
如果您尚未更改offscreenPageLimit
中的ViewPager
,则根据您要去的方向,它总是有3到4个孩子。为了在子元素中显示正确的内容,它使用适配器来获取正确的内容。
现在,当您在removeAllViews()
上调用ViewPager
时,实际上只有3到4个View
从Window
的层次结构中删除,并且通过调用instantiateItem(ViewGroup viewPager, int index)
,您只需要重新创建3到4个View
。然后一切恢复正常,滑动并滚动,ViewPager
使用其适配器显示内容。在所有情况下,其子设备的数量在所有设备上都不相同,例如,如果将offscreenPageLimit
设置为5,则可能会有大约11到12个孩子,但仅此而已。很快。
答案 32 :(得分:0)
覆盖getItemPosition()的正确方法:
@Override
public int getItemPosition(@NonNull Object object) {
if (!(object instanceof MyPageView)) {
// Should never happen
return super.getItemPosition(object);
}
MyDataObject dataObject = ((MyPageView) object).getData();
if (!(dataObject instanceof MyDataObject)) {
// Should never happen
return super.getItemPosition(object);
}
if (lstItems.contains(dataObject)) {
return POSITION_UNCHANGED;
}
return POSITION_NONE;
}
简而言之,您需要将用于绘制视图的数据附加/保存到视图。从那里,调用getItemPosition()时,ViewPager可以确定适配器上是否仍存在当前显示的视图,并采取相应的措施。
答案 33 :(得分:0)
您可以按我在此处的建议执行操作,而不是返回POSITION_NONE
并再次创建所有片段:Update ViewPager dynamically?
答案 34 :(得分:0)
我在这里留下了我自己的解决方案,这是一种解决方法,因为问题似乎是FragmentPagerAdapter
没有清除之前的fragments
,您可以添加到{{1}在片段管理器中。所以,我在添加ViewPager
之前创建了一个执行方法:
(在我的情况下,我从不添加超过3个片段,但您可以使用例如FragmentPagerAdapter
并检查所有getFragmentManager().getBackStackEntryCount()
。
fragments
我知道这是一种解决方法,因为如果框架创建标记的方式发生变化,它将停止工作。
(目前/**
* this method is solving a bug in FragmentPagerAdapter which don't delete in the fragment manager any previous fragments in a ViewPager.
*
* @param containerId
*/
public void cleanBackStack(long containerId) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
for (int i = 0; i < 3; ++i) {
String tag = "android:switcher:" + containerId + ":" + i;
Fragment f = getFragmentManager().findFragmentByTag(tag);
if (f != null) {
transaction.remove(f);
}
}
transaction.commit();
}
)
然后使用它的方法是在获取容器之后:
"android:switcher:" + containerId + ":" + i
答案 35 :(得分:0)
我认为我已经通过简单的方式来通知数据集更改:
首先,按照instantiateItem函数的工作方式改变一下:
@Override
public Object instantiateItem(final ViewGroup container, final int position) {
final View rootView = mInflater.inflate(...,container, false);
rootView.setTag(position);
updateView(rootView, position);
container.addView(rootView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mViewPager.setObjectForPosition(rootView, position);
return rootView;
}
for&#34; updateView&#34; ,在视图中填入您想要填充的所有数据(setText,setBitmapImage,...)。
验证destroyView的工作方式如下:
@Override
public void destroyItem(final ViewGroup container, final int position, final Object obj) {
final View viewToRemove = (View) obj;
mViewPager.removeView(viewToRemove);
}
现在,假设您需要更改数据,执行此操作,然后调用PagerAdapter上的下一个函数:
public void notifyDataSetChanged(final ViewPager viewPager, final NotifyLocation fromPos,
final NotifyLocation toPos) {
final int offscreenPageLimit = viewPager.getOffscreenPageLimit();
final int fromPosInt = fromPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
: fromPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
: mSelectedPhotoIndex + offscreenPageLimit;
final int toPosInt = toPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
: toPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
: mSelectedPhotoIndex + offscreenPageLimit;
if (fromPosInt <= toPosInt) {
notifyDataSetChanged();
for (int i = fromPosInt; i <= toPosInt; ++i) {
final View pageView = viewPager.findViewWithTag(i);
mPagerAdapter.updateView(pageView, i);
}
}
}
public enum NotifyLocation {
MOST_LEFT, CENTER, MOST_RIGHT
}
例如,如果您希望通知viewPager显示的所有视图已发生变化,您可以致电:
notifyDataSetChanged(mViewPager,NotifyLocation.MOST_LEFT,NotifyLocation.MOST_RIGHT);
那就是它。
答案 36 :(得分:0)
对我有用的东西是覆盖finishUpdate
方法,该方法在转换结束时调用并在那里执行notifyDataSetChanged()
:
public class MyPagerAdapter extends FragmentPagerAdapter {
...
@Override
public void finishUpdate(ViewGroup container) {
super.finishUpdate(container);
this.notifyDataSetChanged();
}
...
}
答案 37 :(得分:0)
对于它的价值,在KitKat +上,adapter.notifyDataSetChanged()
似乎足以导致新视图出现,只要你setOffscreenPageLimit
足够高。通过viewPager.setOffscreenPageLimit(2)
我可以获得所需的行为。
答案 38 :(得分:0)
我正在使用Tablayout和ViewPagerAdapter。为了在片段之间传递数据或者在片段之间进行通信,请使用下面的代码,这些代码可以正常工在内部按钮点击第二个片段写下代码。
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String text=e1.getText().toString(); // get the text from EditText
// move from one fragment to another fragment on button click
TabLayout tablayout = (TabLayout) getActivity().findViewById(R.id.tab_layout); // here tab_layout is the id of TabLayout which is there in parent Activity/Fragment
if (tablayout.getTabAt(1).isSelected()) { // here 1 is the index number of second fragment i-e current Fragment
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
Intent i = new Intent("EDIT_TAG_REFRESH");
i.putExtra("MyTextValue",text);
lbm.sendBroadcast(i);
}
tablayout.getTabAt(0).select(); // here 0 is the index number of first fragment i-e to which fragment it has to moeve
}
});
下面是代码,必须在接收片段的第一个片段(在我的例子中)i-e中写入。
MyReceiver r;
Context context;
String newValue;
public void refresh() {
//your code in refresh.
Log.i("Refresh", "YES");
}
public void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(context).unregisterReceiver(r);
}
public void onResume() {
super.onResume();
r = new MyReceiver();
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(r,
new IntentFilter("EDIT_TAG_REFRESH"));
} // this code has to be written before onCreateview()
// below code can be written any where in the fragment
private class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
PostRequestFragment.this.refresh();
String action = intent.getAction();
newValue=intent.getStringExtra("MyTextValue");
t1.setText(newValue); // upon Referesh set the text
}
}
答案 39 :(得分:0)
这是一个可怕的问题,我很乐意提出一个很好的解决方案;简单,高效,有效!
如下所示,代码显示使用标记表示何时返回POSITION_NONE
public class ViewPagerAdapter extends PagerAdapter
{
// Members
private boolean mForceReinstantiateItem = false;
// This is used to overcome terrible bug that Google isn't fixing
// We know that getItemPosition() is called right after notifyDataSetChanged()
// Therefore, the fix is to return POSITION_NONE right after the notifyDataSetChanged() was called - but only once
@Override
public int getItemPosition(Object object)
{
if (mForceReinstantiateItem)
{
mForceReinstantiateItem = false;
return POSITION_NONE;
}
else
{
return super.getItemPosition(object);
}
}
public void setData(ArrayList<DisplayContent> newContent)
{
mDisplayContent = newContent;
mForceReinstantiateItem = true;
notifyDataSetChanged();
}
}
答案 40 :(得分:0)
在我的情况下,每次片段输入到@override onCreateView(...)
时我都会重新启动值,因此处理null或空值,如:
if(var == null){
var = new Something();
}
if(list.isEmpty()){
list = new ArrayList<MyItem>();
}
if(myAdapter == null){
myAdapter = new CustomAdapter();
recyclerView.setAdapter(myAdapter);
}
// etc...
每次我们更改时,片段类都可以进入onCreateView,并在运行时从UI返回到视图。
答案 41 :(得分:-11)
在更新视图之前触发mTabsAdapter.onTabChanged(mTabHost.getCurrentTabTag());
。这将有效。