Android Fragments 如何使用视图模型处理片段的配置更改

11dmarpk  于 2023-10-19  发布在  Android
关注(0)|答案(3)|浏览(125)

我想让我的片段不会重新加载数据时,配置与视图模型的变化。
所以,我试着做一个关于gitHub用户的应用程序。我的主要活动包含搜索视图搜索用户,并显示结果与搜索视图。我的详细活动是显示详细的用户时,在列表中的一个用户点击,并在我的详细活动,我使用标签布局和查看分页显示用户的追随者和以下。
我的ViewModel for activity工作得很好,并且可以在方向改变时保持我的数据。但是当我对我的片段做同样的事情时,当方向改变时,我的片段会不断地重新加载新的数据。
在这里,我使用View Model来请求数据

public class FollowingFragment extends Fragment {

    public static final String KEY_FOLLOWING = "key_following";
    private RecyclerView recyclerView;
    private FollowingViewModel followingViewModel;
    private FollowingAdapter followingAdapter;

    ShimmerFrameLayout shimmerFrameLayout;

    LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
    ArrayList<FollowingResponse> followingResponse = new ArrayList<>();
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_following, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        recyclerView = view.findViewById(R.id.rv_following);
        shimmerFrameLayout = view.findViewById(R.id.shimmer_frame_layout2);

        followingViewModel = ViewModelProviders.of(this).get(FollowingViewModel.class);
        followingAdapter = new FollowingAdapter();
        followingAdapter.setOnItemClickCallback(new FollowingAdapter.OnItemClickCallback() {
            @Override
            public void onItemClicked(FollowingResponse followingResponse) {
                showSelectedItem(followingResponse);
            }
        });

        //this is what i've tried and worked, but my fragment result with double or triple the data.
        if (savedInstanceState == null){

        }
        else {
            followingResponse = savedInstanceState.getParcelableArrayList(KEY_FOLLOWING);
        }

        showRecyclerView();
        getData();
    }

    private void getData() {
        followingViewModel.setDataFollowing(DetailUserActivity.clickedUser);
        followingViewModel.getDataFollowing().observe(getViewLifecycleOwner(), new Observer<ArrayList<FollowingResponse>>() {
            @Override
            public void onChanged(ArrayList<FollowingResponse> followingResponses) {
                shimmerFrameLayout.setVisibility(View.GONE);
                shimmerFrameLayout.stopShimmer();
                followingAdapter.setData(followingResponse);
                followingResponse.addAll(followingResponses);
                recyclerView.setAdapter(followingAdapter);
                followingAdapter.notifyDataSetChanged();
            }
        });
    }
    private void showRecyclerView() {
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(layoutManager);
    }
    @Override
    public void onResume() {
        super.onResume();
        followingViewModel.setDataFollowing(DetailUserActivity.clickedUser);
    }

    private void showSelectedItem(FollowingResponse item) {
        Intent intent = new Intent(getContext(), DetailUserActivity.class);
        intent.putExtra("EXTRA_DATA", item.getLogin());
        startActivity(intent);
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
    }

        @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        outState.putParcelableArrayList(KEY_FOLLOWING, followingResponse);
        super.onSaveInstanceState(outState);
    }
}

详尽活动

public class DetailUserActivity extends AppCompatActivity {
    private TextView tvName, tvNickname, tvLocation, tvCompany, tvEmail, tvWebsite;
    private TextView tvCountFollowers, tvCountRepository, tvCountFollowing;
    private ImageView imgProfile;
    ProgressBar progressBar;

    public static String clickedUser;
    UserViewModel userViewModel;

    private ViewPager viewPager;
    TabLayout tabLayout;

    private final String EXTRA_DATA = "EXTRA_DATA";
    private static final String EXTRA_FOLLOW = "extra_follow";

//    Fragment followingFragment;
//    FragmentTransaction transaction;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.detail_user_activity);

//        i've tried this, but when the orientation change, my detail activity force close and throw me to my main activity.
//        if (savedInstanceState != null){
//            followingFragment = getSupportFragmentManager().getFragment(savedInstanceState, KEY_FOLLOWING);
//        } else {
//            followingFragment = new FollowingFragment();
//
//            getSupportFragmentManager().beginTransaction().replace(R.id.tv_view_pager, followingFragment).commit();
//        }

        tvName = findViewById(R.id.tv_item_name);
        tvNickname = findViewById(R.id.tv_item_nickname);
        tvLocation = findViewById(R.id.tv_item_location);
        tvCompany = findViewById(R.id.tv_item_company);
        tvEmail = findViewById(R.id.tv_item_email);
        tvWebsite = findViewById(R.id.tv_item_website);
        imgProfile = findViewById(R.id.img_item_photo);
        tvCountFollowers = findViewById(R.id.tv_count_follower);
        tvCountRepository = findViewById(R.id.tv_count_repo);
        tvCountFollowing = findViewById(R.id.tv_count_following);
        progressBar = findViewById(R.id.progressbar2);

        setForUserData();
        setForTabLayout();
        
    }

    private void setForUserData(){
        Intent detailIntent = getIntent();
        clickedUser = detailIntent.getStringExtra("EXTRA_DATA");
        userViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(UserViewModel.class);
        userViewModel.setUserUVM(clickedUser);
        userViewModel.getUserUVM().observe(this, new Observer<UserResponse>() {
            @Override
            public void onChanged(UserResponse userResponse) {
                progressBar.setVisibility(View.GONE);
                Glide.with(getApplicationContext())
                        .load(userResponse.getAvatarUrl())
                        .into(imgProfile);
                tvName.setText(userResponse.getLogin());
                tvLocation.setText(userResponse.getLocation());
                tvNickname.setText(userResponse.getName());
                tvCompany.setText(userResponse.getCompany());
                tvEmail.setText(userResponse.getEmail());
                tvWebsite.setText(userResponse.getBlog());

                tvCountRepository.setText(String.valueOf(userResponse.getPublicRepos()));
                tvCountFollowers.setText(String.valueOf(userResponse.getFollowers()));
                tvCountFollowing.setText(String.valueOf(userResponse.getFollowing()));
            }
        });
    }

    
    private void setForTabLayout(){
        tabLayout = findViewById(R.id.tv_tab_layout);
        viewPager = findViewById(R.id.tv_view_pager);
        viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount()));
        viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
        tabLayout.setTabMode(TabLayout.MODE_FIXED);

        tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                viewPager.setCurrentItem(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
            }
        });
    }

    public class ViewPagerAdapter extends FragmentStatePagerAdapter {
        int totalTab;
        @SuppressWarnings("deprecation")
        ViewPagerAdapter(FragmentManager fragmentManager, int totalTabs) {
            super(fragmentManager);
            this.totalTab = totalTabs;
        }

        @SuppressWarnings("ConstantConditions")
        @NonNull
        @Override
        public Fragment getItem(int position) {
            switch (position){
                case 0:
                    return new FollowersFragment();
                case 1:
                    return new FollowingFragment();
                default:
                    return null;
            }
        }

        @Override
        public int getCount() {
            return totalTab;
        }
    }

//    @Override
//    protected void onSaveInstanceState(@NonNull Bundle outState) {
//       getSupportFragmentManager().putFragment(outState, KEY_FOLLOWING, followingFragment );
//       super.onSaveInstanceState(outState);
//    }
}

我试图解决我的FollowingFragment第一,然后我会做同样的对我的FollowerFragment。我真的是编程新手,没有人可以问。有人可以帮助我解决我的问题与保持数据在片段时,方向改变?

lymnna71

lymnna711#

followingViewModel = ViewModelProviders.of(this).get(FollowingViewModel.class);

在Fragment中,而不是在onViewCreated()中编写此代码,将其写入OnCreate()中
onViewCreated()将在每次方向改变时被调用,因此它将在每次方向改变时创建视图模型的新示例,从而使视图模型重新加载数据

ttisahbt

ttisahbt2#

如果您只需要为FollowingViewModel加载一次数据,那么最好在视图模型的构造函数中进行加载,这样您就不必担心片段的方向更改。

dgiusagp

dgiusagp3#

当您在片段中创建ViewModel的示例时,如下所示
followingViewModel = ViewModelProviders.of(this).get(FollowingViewModel.class);
您将this作为所有者传递,其中这是片段本身。要获取View Model的同一示例,需要传递所有者的同一示例。
每当方向发生变化时,碎片就会被破坏并重新创建。在重新创建时,this是一个不同的示例,因此提供程序返回的ViewModel也不同。
你可以做的是,使用片段的父Activity作为所有者。
followingViewModel = ViewModelProviders.of(activity).get(FollowingViewModel.class);
由于大多数情况下Activity不会被销毁,因此您将收到相同的FollowingViewModel示例。
在Activity被销毁的情况下,新Activity能够使用NonConfigurationInstances检索相同的旧ViewModelStore。因此,在重建时获取相同的视图模型。这就是为什么UserViewModel逻辑在Activity中正常工作的原因。
点击此链接了解更多详情proandroiddev

相关问题