简体   繁体   English

如何在 ViewPager 和 TabLayout 中多次使用相同的 Fragment 类?

[英]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.问题是只有在创建单个选项卡时才有效,当应该创建 2 个或更多选项卡时,应用程序崩溃,因为 GraphFragment 内的RelativeLayout上的空引用,此相对布局用于嵌入图表。

here is xml files这是xml文件

activity_main.xml活动_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 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 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主活动.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?我不明白为什么使用 1 个选项卡它可以正常工作,但是出现 2 个或更多 NullPointerException,有什么想法吗? Thank you!谢谢!

To create a dynamic FragmentPagerAdapter, you need to use the following code:要创建一个动态的 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");
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 同一片段中的多个ViewPager - Multiple ViewPager in same Fragment Android中ViewPager2如何使用TabLayout - How to use TabLayout with ViewPager2 in Android 如何使用TabLayout在ViewPager中的Google Maps Fragment中获取当前位置 - How to get current location within a Google Maps Fragment in a ViewPager with TabLayout 选择不同的选项卡时如何在 ViewPager + TabLayout 中暂停片段? - How to pause a fragment in ViewPager + TabLayout when a different tab is selected? 在相同的活动/布局中多次使用相同的片段 - Use same fragment multiple times in same activity/layout 如何跨viewPager重用相同片段 - How to re-use same fragment across viewPager ViewPager 中同一 Fragment 的多个实例有问题吗? - Issues with multiple instances of same Fragment in ViewPager? 从第二个Fragment的recyclerView中选择项目时如何切换到第一个Fragment? 两个片段都在带有 viewPager2 的 tablayout 中 - How to switch to the first fragment when selecting an item from the recyclerView of second Fragment? Both fragments are in tablayout with viewPager2 在ViewPager中使用相同的片段,但片段每次都会有不同的布局 - Use same fragment in ViewPager but fragment will have different layout each time 如何在viewpager中同时显示2个片段? - How to have 2 fragment showing at the same time in a viewpager?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM