我正在使用兼容性库中的ViewPager。我已经成功地让它显示几个视图,我可以通过页面。
但是,我很难弄清楚如何用一组新的视图更新ViewPager。
我已经尝试了各种各样的事情,比如调用mAdapter.notifyDataSetChanged(), mviewpage .invalidate(),甚至在每次我想使用新的数据列表时创建一个全新的适配器。
没有任何帮助,textviews保持不变,从原始数据。
更新:
我做了一个小测试项目,我几乎能够更新视图。我将在下面粘贴这个类。
然而,似乎没有更新的是第二个视图,“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) {
}
}
}
在OP提出他的问题两年半之后,这个问题仍然,嗯,仍然是一个问题。显然谷歌在这方面的优先级不是特别高,所以我没有找到解决方案,而是找到了一个变通办法。对我来说,最大的突破是找到了问题的真正原因(见本文中公认的答案)。一旦问题明显是任何活动页面都没有正确刷新,我的解决方法就很明显了:
在我的片段(几页)中:
I took all the code which populates the form out of onCreateView and put it in a function called PopulateForm which may be called from anywhere, rather than by the framework. This function attempts to get the current View using getView, and if that is null, it just returns. It's important that PopulateForm contains only the code that displays - all the other code which creates FocusChange listeners and the like is still in OnCreate
Create a boolean which can be used as a flag indicating the form must be reloaded. Mine is mbReloadForm
Override OnResume() to call PopulateForm() if mbReloadForm is set.
在我的活动中,我做页面的加载:
Go to page 0 before changing anything. I'm using FragmentStatePagerAdapter, so I know that two or three pages are affected at most. Changing to page 0 ensures I only ever have the problem on pages 0, 1 and 2.
Before clearing the old list, take it's size(). This way you know how many pages are affected by the bug. If > 3, reduce it to 3 - if you're using a a different PagerAdapter, you'll have to see how many pages you have to deal with (maybe all?)
Reload the data and call pageAdapter.notifyDataSetChanged()
Now, for each of the affected pages, see if the page is active by using pager.getChildAt(i) - this tells you if you have a view. If so, call pager.PopulateView(). If not, set the ReloadForm flag.
在此之后,当您重新加载第二组页面时,该错误仍然会导致一些页面显示旧数据。但是,现在它们将被刷新,您将看到新的数据-您的用户不会知道页面是不正确的,因为这种刷新将在他们看到页面之前发生。
希望这能帮助到一些人!
我使用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
}
}
ViewPager不是为支持动态视图更改而设计的。
我已经确认了这一点,同时寻找与此相关的另一个bug https://issuetracker.google.com/issues/36956111,特别是https://issuetracker.google.com/issues/36956111#comment56
这个问题有点老了,但是谷歌最近用ViewPager2解决了这个问题。
它将允许用标准的解决方案取代手工的(不需要维护并且可能有bug的)解决方案。它还可以防止像某些回答那样不必要地重新创建视图。
对于ViewPager2的例子,您可以查看https://github.com/googlesamples/android-viewpager2
如果您想使用ViewPager2,您需要在构建中添加以下依赖项。Gradle文件:
dependencies {
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
}
然后你可以替换你的ViewPager在你的xml文件:
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
在那之后,你需要在你的活动中用ViewPager2替换ViewPager
ViewPager2需要一个RecyclerView。适配器,或者FragmentStateAdapter,在你的例子中,它可以是RecyclerView。适配器
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方法来刷新视图
好了,伙计们,我用instantiateItem找到了一个解决方案。11年后再看这个视频的人:
viewModel.mImage.observe(this, imagePath -> {
int pos = binding.sliderIntro.getCurrentPagePosition();
adapter.editList(pos, imagePath);
if (binding.sliderIntro.findViewWithTag("" + pos) != null)
adapter.instantiateItem(binding.sliderIntro.findViewWithTag("" + pos), pos);
});
以及您的适配器:
public void editList(int position, String imagePath) {
this.sliderItems.set(position, imagePath);
}
and:
@Override
public void onBindViewHolder(IntroSliderAdapterViewHolder viewHolder, final int position) {
viewHolder.itemView.setTag("" + position);
}
1.首先,您必须在Pageradapter类中设置getItemposition方法
2.您必须阅读您的视图寻呼机的确切位置
3.然后将该位置作为新位置的数据位置发送
4.在setonPageChange监听器中写入更新按钮的点击监听器
我修改了程序代码,只设置了特定的位置元素
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) {
}
}
}