java - 如何在 ViewPager 和 TabLayout 中多次使用相同的 Fragment 类?
问题描述
所以,我试图根据一些值在运行时创建多个选项卡,每个选项卡都包含一个片段,每个片段都包含一些图表。问题是只有在创建单个选项卡时才有效,当应该创建 2 个或更多选项卡时,应用程序崩溃,因为RelativeLayout
GraphFragment 内部的空引用,此相对布局用于嵌入图表。
这是xml文件
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:background="@color/white"
app:tabSelectedTextColor="@color/colorAccent"
app:tabTextColor="@color/colorPrimary"
app:tabGravity="center"
app:tabMode="scrollable"
app:tabIndicatorHeight="3dp">
</com.google.android.material.tabs.TabLayout>
<com.example.cargraph.CustomViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/viewPager"/>
</androidx.appcompat.widget.LinearLayoutCompat>
片段图.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".GraphFragment"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/graphLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp">
</RelativeLayout>
</FrameLayout>
这是开始创建选项卡和片段的代码
数据检索器.java
for(int i=0; i<allData.length(); i++){
JSONObject stationData = allData.getJSONObject(i);
JSONObject graphData = stationData.getJSONObject("data");
ChartBuilder cb = new ChartBuilder(
graphData.getJSONObject("date_time"),
graphData.getJSONObject("values"),
"label",
activity, //this is a reference to main activity
stationsList.get(i)
);
cb.createChart();
}
ChartBuilder.java
public class ChartBuilder {
private JSONObject xValues;
private JSONObject yValues;
private String label;
private LineChart lineChart;
private MainActivity mActivity;
private String tabLabel;
public ChartBuilder(JSONObject xValues, JSONObject yValues, String label,
MainActivity mActivity, String tabLabel) {
this.xValues = xValues;
this.yValues = yValues;
this.label = label;
this.lineChart = new LineChart(mActivity);
this.mActivity = mActivity;
this.tabLabel = tabLabel;
}
private void createTab(){
GraphFragment graphFragment = new GraphFragment();
mActivity.addGraphFragment(graphFragment, tabLabel);
RelativeLayout graphLayout = graphFragment.getGraphLayout(); //this is the nullPointerException when allData.length() > 2 in DataRetriver.java
graphLayout.addView(lineChart);
}
private LineDataSet createDataSet() throws JSONException {
List<Entry> entries = new ArrayList<Entry>();
JSONArray names = xValues.names();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
long timeReference = 0;
long newXvalue;
for(int i=0; i<names.length(); i++){
try{
if(i==0){
Date date = simpleDateFormat.parse(xValues.getString(names.getString(i)));
timeReference = date.getTime();
}
Date date = simpleDateFormat.parse(xValues.getString(names.getString(i)));
Timestamp timestamp = new Timestamp(date.getTime());
newXvalue = timestamp.getTime()- timeReference;
entries.add(new Entry(newXvalue, yValues.getLong(names.getString(i))));
}catch (ParseException p){
System.out.println(p.toString());
}
}
LineDataSet lineDataSet = new LineDataSet(entries, this.label);
lineDataSet.setColor(R.color.colorPrimary);
lineDataSet.setValueTextColor(R.color.colorAccent);
return lineDataSet;
}
public void createChart() throws JSONException {
LineData lineData = new LineData(createDataSet());
this.lineChart.setData(lineData);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT
);
this.lineChart.setLayoutParams(layoutParams);
this.lineChart.invalidate();
createTab();
}
}
GraphFragment.java
public class GraphFragment extends Fragment {
private RelativeLayout graphLayout;
public GraphFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_graph, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
graphLayout = getView().findViewById(R.id.graphLayout);
}
public RelativeLayout getGraphLayout() {
return graphLayout;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private TabLayout tabLayout;
private CustomViewPager viewPager;
private ViewPagerAdapter viewPagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupView();
setupViewPagerAdapter(new HomeFragment());
}
private void setupView(){
tabLayout = findViewById(R.id.tabLayout);
viewPager = findViewById(R.id.viewPager);
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
}
private void setupViewPagerAdapter(Fragment fragment){
viewPagerAdapter.addFragment(fragment, "Inicio");
viewPager.setAdapter(viewPagerAdapter);
viewPager.setPagingEnabled(false);
tabLayout.setupWithViewPager(viewPager, true);
}
public void addGraphFragment(Fragment fragment, String title){
viewPagerAdapter.addFragment(fragment, title);
viewPagerAdapter.notifyDataSetChanged();
}
public void removeGraphFragment(){
viewPagerAdapter.removeFragments();
viewPagerAdapter.notifyDataSetChanged();
}
public ViewPagerAdapter getViewPagerAdapter() {
return viewPagerAdapter;
}
}
我不明白为什么使用 1 个选项卡它工作得很好,但是出现 2 个或更多 NullPointerException,有什么想法吗?谢谢!
解决方案
要创建动态 FragmentPagerAdapter,需要使用以下代码:
public class DynamicFragmentPagerAdapter extends PagerAdapter {
private static final String TAG = "DynamicFragmentPagerAdapter";
private final FragmentManager fragmentManager;
public static abstract class FragmentIdentifier implements Parcelable {
private final String fragmentTag;
private final Bundle args;
public FragmentIdentifier(@NonNull String fragmentTag, @Nullable Bundle args) {
this.fragmentTag = fragmentTag;
this.args = args;
}
protected FragmentIdentifier(Parcel in) {
fragmentTag = in.readString();
args = in.readBundle(getClass().getClassLoader());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(fragmentTag);
dest.writeBundle(args);
}
protected final Fragment newFragment() {
Fragment fragment = createFragment();
Bundle oldArgs = fragment.getArguments();
Bundle newArgs = new Bundle();
if(oldArgs != null) {
newArgs.putAll(oldArgs);
}
if(args != null) {
newArgs.putAll(args);
}
fragment.setArguments(newArgs);
return fragment;
}
protected abstract Fragment createFragment();
}
private ArrayList<FragmentIdentifier> fragmentIdentifiers = new ArrayList<>();
private FragmentTransaction currentTransaction = null;
private Fragment currentPrimaryItem = null;
public DynamicFragmentPagerAdapter(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
}
private int findIndexIfAdded(FragmentIdentifier fragmentIdentifier) {
for (int i = 0, size = fragmentIdentifiers.size(); i < size; i++) {
FragmentIdentifier identifier = fragmentIdentifiers.get(i);
if (identifier.fragmentTag.equals(fragmentIdentifier.fragmentTag)) {
return i;
}
}
return -1;
}
public void addFragment(FragmentIdentifier fragmentIdentifier) {
if (findIndexIfAdded(fragmentIdentifier) < 0) {
fragmentIdentifiers.add(fragmentIdentifier);
notifyDataSetChanged();
}
}
public void removeFragment(FragmentIdentifier fragmentIdentifier) {
int index = findIndexIfAdded(fragmentIdentifier);
if (index >= 0) {
fragmentIdentifiers.remove(index);
notifyDataSetChanged();
}
}
@Override
public int getCount() {
return fragmentIdentifiers.size();
}
@Override
public void startUpdate(@NonNull ViewGroup container) {
if (container.getId() == View.NO_ID) {
throw new IllegalStateException("ViewPager with adapter " + this
+ " requires a view id");
}
}
@SuppressWarnings("ReferenceEquality")
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
final FragmentIdentifier fragmentIdentifier = fragmentIdentifiers.get(position);
// Do we already have this fragment?
final String name = fragmentIdentifier.fragmentTag;
Fragment fragment = fragmentManager.findFragmentByTag(name);
if (fragment != null) {
currentTransaction.attach(fragment);
} else {
fragment = fragmentIdentifier.newFragment();
currentTransaction.add(container.getId(), fragment, fragmentIdentifier.fragmentTag);
}
if (fragment != currentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
currentTransaction.detach((Fragment) object);
}
@SuppressWarnings("ReferenceEquality")
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment) object;
if (fragment != currentPrimaryItem) {
if (currentPrimaryItem != null) {
currentPrimaryItem.setMenuVisibility(false);
currentPrimaryItem.setUserVisibleHint(false);
}
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
currentPrimaryItem = fragment;
}
}
@Override
public void finishUpdate(@NonNull ViewGroup container) {
if (currentTransaction != null) {
currentTransaction.commitNowAllowingStateLoss();
currentTransaction = null;
}
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return ((Fragment) object).getView() == view;
}
@Override
public Parcelable saveState() {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("fragmentIdentifiers", fragmentIdentifiers);
return bundle;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
Bundle bundle = ((Bundle)state);
bundle.setClassLoader(loader);
fragmentIdentifiers = bundle.getParcelableArrayList("fragmentIdentifiers");
}
}
推荐阅读
- c++ - SQL 失败 - 没有返回值的文档
- flutter - Flutter chrome 调试无法启动
- firebase - 基于主题的 Firebase Firestore 聊天消息
- android - 如何将带有数据类的数据从对话框片段传递到另一个带有kotlin接口的片段?
- python - IndexError: list index out of range if words[i+1] in vector: for c in range(len(vector)):
- c++ - QT4:如何从ui生成头文件
- python - 从坐标和密度列表中制作密度图
- node.js - 在 NODEJS 中将“2021 年 6 月 9 日,下午 1:50:00”转换为 2021-06-09 13:50:00 日期格式
- java - Hibernate 自动查询
- angularjs - 在AngularJS项目的ui-grid中呈现所有行后如何执行函数?