简体   繁体   中英

How to use same Fragment class multiple times in a ViewPager and TabLayout?

So, i'm trying to create multiple tabs on runtime depending on some values, each tab contains a fragment, and each fragment contains some charts. the problem is that only works when a single tab is created, when 2 or more tabs are supposed to be created the app crash because a null reference on a RelativeLayout inside the GraphFragment, this relative layout is used to embedded the chart.

here is xml files

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>

fragment_graph.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>

this is the code that starts the create tabs and fragments

DataRetriver.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;
    }
}

I dont get why with 1 tab it works just fine, but 2 or more the NullPointerException appears, any ideas? Thank you!

To create a dynamic FragmentPagerAdapter, you need to use the following code:

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");
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM