Черный экран виден после переключения на другой фрагмент

#java #android #android-fragments #fragment

#java #Android #android-фрагменты #фрагмент

Вопрос:

Я пытаюсь создать простое приложение со списком продуктов и подробной информацией о продукте. Что касается моих первоначальных попыток, я создал приложение, используя BottomNavigationView, где внизу есть маленькие значки, и вы можете переключать фрагменты, нажимая на один из них. В настоящее время у меня есть 4 значка на нижней панели навигации, и вот 4 значка:

  • Продукты (ProductsFragment)
  • Избранное (FavoritesFragment)
  • Профиль (ProfileFragment)
  • Корзина (CartFragment)

Эти 4 фрагмента переключаются в моем HomeActivity, вот как я их переключаю:

HomeActivity.java

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

        bottomNavbar = findViewById(R.id.nav_view);
        bottomNavbar.setOnNavigationItemSelectedListener(navListener);
        homeContainer = findViewById(R.id.homeContainer);
        createSnackbar(homeContainer);
        viewModel = new HomeViewModel();
        viewModel.getCartCounterLiveData().observe(this, CartCounterObserver);
        createBadges(bottomNavbar);
}    

private BottomNavigationView.OnNavigationItemSelectedListener navListener = new BottomNavigationView.OnNavigationItemSelectedListener() {

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            BaseRTFFragment selectedFragment = null;
            String stackName;

            switch(item.getItemId()) {
                case R.id.nav_home:
                    selectedFragment = new ProductsFragment();
                    break;

                case R.id.nav_favorites:
                    selectedFragment = new FavoritesFragment();
                    break;

                case R.id.nav_profile:
                    selectedFragment = new ProfileFragment();
                    break;

                case R.id.nav_cart:
                    selectedFragment = new CartFragment();
                    break;
            }

            navigateToFragment(selectedFragment);
            return true;
        }
    };

    private void navigateToFragment(BaseRTFFragment selectedFragment) {
        FragmentTransaction fTrans = getSupportFragmentManager().beginTransaction();
        // fTrans.addToBackStack(selectedFragment.getFragmentName());
        fTrans.replace(R.id.nav_host_fragment, selectedFragment).commit();
    }

  

Так что это всего лишь простой вызов replace() . На данный момент он работает нормально, фрагменты переключаются без черного экрана между ними

Кстати, во фрагменте PRODUCTS есть текст поиска, кнопки фильтра категорий и разбитое на страницы RecyclerView для отображения списка продуктов. Смотрите код ниже:

ProductsFragment.java

 public class ProductsFragment extends BaseRTFFragment<FragmentProductsBinding>
        implements Observer<PagedList<Product>>, ProductsAdapter.OnProductClickListener, CategoryAdapter.OnCategoryClickListener {

    ProductsViewModel viewModel;
    ProductsAdapter productsAdapter;
    int selectedCategory;
    String currentSearchText;
    ProductsEventsListener listener;

    private CategoryAdapter categoryAdapter;
    private String fragmentName = "Products";

    @Override
    protected int getFragmentLayout() {
        return R.layout.fragment_products;
    }

    @Override
    public String getFragmentName() { return fragmentName; }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        viewModel = new ProductsViewModel();
        initProductsAdapter();
        initCategoryAdapter();
        initializeSearch();
        loadProducts();
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        try {
            listener = (ProductsEventsListener) context;
        } catch (ClassCastException castException) {
            throw new Error("The activity does not implement the listener");
        }
    }

    private void initProductsAdapter() {
        productsAdapter = new ProductsAdapter(this);
        dataBinding.productsRecyclerView.setAdapter(productsAdapter);
    }

    private void initCategoryAdapter() {
        Query cQuery = initCategoryQuery();
        cQuery.get().addOnCompleteListener(categoryListener);
    }

    private OnCompleteListener<QuerySnapshot> categoryListener = new OnCompleteListener<QuerySnapshot>() {
        @Override
        public void onComplete(@NonNull Task<QuerySnapshot> task) {
            if(task.isSuccessful()) {
                List<Category> categories = task.getResult().toObjects(Category.class);
                categoryAdapter = getCategoryAdapter(categories);
                dataBinding.categoryRecyclerView.setAdapter(categoryAdapter);
            }
        }
    };

    private CategoryAdapter getCategoryAdapter(List<Category> categories) {
        return new CategoryAdapter(categories, this);
    }

    private Query initCategoryQuery() {
        FirebaseFirestore mFirestore = FirebaseFirestore.getInstance();
        return mFirestore.collection("categories")
                .orderBy("name", Query.Direction.ASCENDING);
    }

    private void loadProducts() {
        viewModel.pagedListLiveData.observe(getViewLifecycleOwner(), this);
    }

    private void reloadProducts() {
        viewModel.replaceSubscription(this, null, 0);
        loadProducts();
    }

    private void loadSearchedProducts(String searchText, int category) {
        viewModel.replaceSubscription(this, searchText, category);
        loadProducts();
    }

    @Override
    public void onChanged(PagedList<Product> products) {
        productsAdapter.submitList(products);
        hideProgressBar();
        // toggleEmptyLayout(products.size());
    }

    private void toggleEmptyLayout(int count) {
        if(count > 0) {
            dataBinding.productsRecyclerView.setVisibility(View.VISIBLE);
            dataBinding.emptyResultLayout.setVisibility(View.GONE);
        } else {
            dataBinding.productsRecyclerView.setVisibility(View.GONE);
            dataBinding.emptyResultLayout.setVisibility(View.VISIBLE);
        }
    }

    private void hideProgressBar() {
        dataBinding.progressBar.setVisibility(View.GONE);
    }

    public int getSelectedCategory() {
        return selectedCategory;
    }

    @Override
    public void onProductClick(Product product) {
        displayProductName(product.getName()   " has been clicked!");
        listener.OnProductClick(product);
    }

    @Override
    public void onAddProductToCart(Product product) {
        // displayProductName(product.getName()   " has been added to cart");
        listener.OnAddProductToCart(product);
    }

    @Override
    public void onFavoriteProduct(Product product) {
        listener.OnFavoriteProduct(product);
    }

    private void displayProductName(String name) {
        Toast.makeText(getActivity(), name, Toast.LENGTH_SHORT).show();
    }

    private void initializeSearch() {
        dataBinding.searchInput.addTextChangedListener(searchTextWatcher);
    }

    private TextWatcher searchTextWatcher = new TextWatcher() {
        @Override
        public void afterTextChanged(Editable searchText) {
            String sText = searchText.toString().toLowerCase();;
            if (sText != currentSearchText) {
                currentSearchText = sText;
                loadSearchedProducts(currentSearchText, selectedCategory);
            }
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start,
                                      int count, int after) {
        }

        @Override
        public void onTextChanged(CharSequence s, int start,
                                  int before, int count) {
        }
    };

    @Override
    public void onCategoryClick(int selectedCategoryId) {
        selectedCategory = selectedCategoryId;
        loadSearchedProducts(currentSearchText, selectedCategory);
    }
}
  

All of my Fragments inherits a custom base fragment which just inherits from the android Fragment, see code below:

BaseRTFFragment.java

 public abstract class BaseRTFFragment<T extends ViewDataBinding> extends Fragment {
    protected T dataBinding;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle bundle) {
        dataBinding = DataBindingUtil.inflate(inflater, getFragmentLayout(), null, false);
        return dataBinding.getRoot();
    }

    protected abstract int getFragmentLayout();
    public abstract String getFragmentName();

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        dataBinding = null;
    }
}
  

So now everything is fine, its now time to add the product detail page.
For this one, I’ve created a new ProductFragment (take note no ‘s’ in the name :))

ProductFragment.java

 
public class ProductFragment extends BaseRTFFragment<FragmentProductBinding> implements Observer<Product> {

    ProductViewModel viewModel;
    ProductEventsListener listener;
    private String fragmentName = "Product";

    @Override
    protected int getFragmentLayout() {
        return R.layout.fragment_product;
    }

    @Override
    public String getFragmentName() { return fragmentName; }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle bundle) {
        int productId = getArguments().getInt("productId");
        viewModel = new ProductViewModel(productId);
        viewModel.getProduct(productId).observe(getViewLifecycleOwner(), this);
        return super.onCreateView(inflater, container, bundle);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        try {
            listener = (ProductEventsListener) context;
        } catch (ClassCastException castException) {
            throw new Error("The activity does not implement the listener");
        }
    }

    @Override
    public void onChanged(Product product) {
        dataBinding.productTitle.setText(product.getName());
    }

}
  

For the layout for this fragment, see the code below:

fragment_product.xml

 <?xml version="1.0" encoding="utf-8"?>
<layout 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">
    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:id="@ id/productFragmentLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.google.android.material.appbar.AppBarLayout
            android:id="@ id/productAppBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorPrimary"
            android:fitsSystemWindows="true">
            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:id="@ id/productCollapseToolbar"
                android:layout_width="match_parent"
                android:layout_height="256dp"
                android:fitsSystemWindows="true"
                app:contentScrim="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
                app:titleEnabled="false">

                <ImageView
                    android:id="@ id/htab_header"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:background="@drawable/ic_default_product"
                    android:fitsSystemWindows="true"
                    android:scaleType="centerCrop"
                    app:layout_collapseMode="parallax"/>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:alpha="0.3"
                    android:background="@android:color/black"
                    android:fitsSystemWindows="true"/>

                <ImageButton
                    android:id="@ id/backBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@color/colorPrimary"
                    android:tint="@color/colorSecondary"
                    android:padding="@dimen/btn_padding"
                    android:layout_margin="@dimen/btn_padding"
                    android:onClick="OnNavigateBack"
                    android:src="@drawable/ic_previous"/>

            </com.google.android.material.appbar.CollapsingToolbarLayout>
        </com.google.android.material.appbar.AppBarLayout>
        <androidx.core.widget.NestedScrollView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="@dimen/default_page_padding"
                android:orientation="vertical">
                <TextView
                    android:id="@ id/productTitle"
                    style="@style/ProductTitle"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_marginBottom="@dimen/btn_padding"
                    android:text="Growers Mix"/>
                <TextView
                    android:id="@ id/productDescription"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:text="Test"/>
            </LinearLayout>
        </androidx.core.widget.NestedScrollView>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
  

Обратите внимание на ImageButton, я объявил метод OnNavigateBack в HomeActivity.java потому что, объявляя об этом на ProductFragment.java вызывает некоторые ошибки, говорящие о том, что он не может найти подобный метод, и поэтому я объявляю его в HomeActivity. Вот как это выглядит:

HomeActivity.java:OnNavigateBack

 @Override
public void OnNavigateBack() {
   navigateToFragment(new ProductsFragment());
}

private void navigateToFragment(BaseRTFFragment selectedFragment) {
        FragmentTransaction fTrans = getSupportFragmentManager().beginTransaction();
        // fTrans.addToBackStack(selectedFragment.getFragmentName());
        fTrans.replace(R.id.nav_host_fragment, selectedFragment).commit();
    }
@Override
    public void OnProductClick(Product product) {
        // displayProductName(product.getName()   " should be viewed in a separate fragment");
        Bundle bundle = new Bundle();
        bundle.putInt("productId", product.getId());
        ProductFragment productFragment = new ProductFragment();
        productFragment.setArguments(bundle);
        navigateToFragment(productFragment);
    }
  

Это просто вызов метода navigateToFragment() из предыдущего. Но на этот раз каждый раз, когда я нажимаю кнопку «Назад» из ProductFragment, он отображает черный экран не менее 1 секунды и загружает список продуктов. Я не уверен, что кажется неправильным, потому что я просто загружаю фрагмент так же, как и другие. Я что-то пропустил? Или подход, который я сделал, был неправильным? Что мне делать? Пожалуйста, помогите. Заранее спасибо.

Ответ №1:

Найдено решение по этому вопросу, похоже, привязка не работает должным образом с использованием свойства android: onClick в макете фрагмента. Поэтому я возвращаюсь к старому способу привязки событий щелчка, связывая его, устанавливая OnClickListener на кнопке «Назад»:

СДЕЛАТЬ:

ProductFragment.java

  @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        bindEvents();
    }

    private void bindEvents() {
        dataBinding.backBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                listener.onNavigateBack();
            }
        });
    }
  

Я НЕ ДЕЛАЮ С ЭТОГО МОМЕНТА НИКОГДА:

fragment_product.xml

 ...
<ImageButton
    ....
    android:id="@ id/backBtn"
    android:onClick="onProductClick"
    ... />
...