黑屏在切换到另一个片段后可见。

huangapple go评论108阅读模式
英文:

Black Screen is visible after switching to another Fragment

问题

I'm trying to create a simple application with products list and a product master detail.
As for my initial attempts, I have created the app using a BottomNavigationView, where you have small icons at the bottom and you can switch fragments by clicking one of them. I currently have 4 icons at the bottomNavigationBar and here are the 4 icons:

  • Products (ProductsFragment)
  • Favorites (FavoritesFragment)
  • Profile (ProfileFragment)
  • Cart (CartFragment)

These 4 fragments are switched in my HomeActivity, this is how I switch them:

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.replace(R.id.nav_host_fragment, selectedFragment).commit();
}

The ProductsFragment has a searchText, category filter buttons, and a paginated recyclerView to display the list of products.

ProductsFragment.java

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

    // ... (rest of the code)
}

The base fragment class:

BaseRTFFragment.java

public abstract class BaseRTFFragment<T extends ViewDataBinding> extends Fragment {
    // ... (rest of the code)
}

You want to add a product detail page, so you've created a new ProductFragment:

ProductFragment.java

public class ProductFragment extends BaseRTFFragment<FragmentProductBinding> implements Observer<Product> {
    // ... (rest of the code)
}

This is the layout for the product detail fragment:

fragment_product.xml

<!-- ... (rest of the layout code) -->

You've added an ImageButton in the product detail layout that should navigate back to the product list:

HomeActivity.java:OnNavigateBack

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

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

@Override
public void OnProductClick(Product product) {
    Bundle bundle = new Bundle();
    bundle.putInt("productId", product.getId());
    ProductFragment productFragment = new ProductFragment();
    productFragment.setArguments(bundle);
    navigateToFragment(productFragment);
}

However, when you navigate back from the product detail page to the product list, there's a black screen displayed for about a second before the list of products is loaded.

You're not sure what's causing this issue, as you're loading the fragment in the same way as the others. You're seeking advice on what you might have missed or if your approach is incorrect. Could you please help? Thanks in advance.

英文:

I'm trying to create a simple application with products list and a product master detail.
As for my initial attempts, I have created the app using a BottomNavigationView, where you have small icons at the bottom and you can switch fragments by clicking one of them. I currently have 4 icons at the bottomNavigationBar and here are the 4 icons:

  • Products (ProductsFragment)
  • Favorites (FavoritesFragment)
  • Profile (ProfileFragment)
  • Cart(CartFragment)

These 4 fragments are switch in my HomeActivity, this is how I switch them:

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

So it is just a simple call to replace(). At this point its working fine, the fragments are switched without a black screen in between

By the way, the ProductsFragment has a searchText, category filter buttons and a paginated recyclerView to display the list of products. See the code below:

ProductsFragment.java

public class ProductsFragment extends BaseRTFFragment&lt;FragmentProductsBinding&gt;
implements Observer&lt;PagedList&lt;Product&gt;&gt;, ProductsAdapter.OnProductClickListener, CategoryAdapter.OnCategoryClickListener {
ProductsViewModel viewModel;
ProductsAdapter productsAdapter;
int selectedCategory;
String currentSearchText;
ProductsEventsListener listener;
private CategoryAdapter categoryAdapter;
private String fragmentName = &quot;Products&quot;;
@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(&quot;The activity does not implement the listener&quot;);
}
}
private void initProductsAdapter() {
productsAdapter = new ProductsAdapter(this);
dataBinding.productsRecyclerView.setAdapter(productsAdapter);
}
private void initCategoryAdapter() {
Query cQuery = initCategoryQuery();
cQuery.get().addOnCompleteListener(categoryListener);
}
private OnCompleteListener&lt;QuerySnapshot&gt; categoryListener = new OnCompleteListener&lt;QuerySnapshot&gt;() {
@Override
public void onComplete(@NonNull Task&lt;QuerySnapshot&gt; task) {
if(task.isSuccessful()) {
List&lt;Category&gt; categories = task.getResult().toObjects(Category.class);
categoryAdapter = getCategoryAdapter(categories);
dataBinding.categoryRecyclerView.setAdapter(categoryAdapter);
}
}
};
private CategoryAdapter getCategoryAdapter(List&lt;Category&gt; categories) {
return new CategoryAdapter(categories, this);
}
private Query initCategoryQuery() {
FirebaseFirestore mFirestore = FirebaseFirestore.getInstance();
return mFirestore.collection(&quot;categories&quot;)
.orderBy(&quot;name&quot;, 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&lt;Product&gt; products) {
productsAdapter.submitList(products);
hideProgressBar();
// toggleEmptyLayout(products.size());
}
private void toggleEmptyLayout(int count) {
if(count &gt; 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() + &quot; has been clicked!&quot;);
listener.OnProductClick(product);
}
@Override
public void onAddProductToCart(Product product) {
// displayProductName(product.getName() + &quot; has been added to cart&quot;);
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&lt;T extends ViewDataBinding&gt; 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&lt;FragmentProductBinding&gt; implements Observer&lt;Product&gt; {
ProductViewModel viewModel;
ProductEventsListener listener;
private String fragmentName = &quot;Product&quot;;
@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(&quot;productId&quot;);
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(&quot;The activity does not implement the listener&quot;);
}
}
@Override
public void onChanged(Product product) {
dataBinding.productTitle.setText(product.getName());
}
}

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

fragment_product.xml

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;layout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
xmlns:tools=&quot;http://schemas.android.com/tools&quot;&gt;
&lt;androidx.coordinatorlayout.widget.CoordinatorLayout
android:id=&quot;@+id/productFragmentLayout&quot;
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;match_parent&quot;&gt;
&lt;com.google.android.material.appbar.AppBarLayout
android:id=&quot;@+id/productAppBar&quot;
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;wrap_content&quot;
android:background=&quot;@color/colorPrimary&quot;
android:fitsSystemWindows=&quot;true&quot;&gt;
&lt;com.google.android.material.appbar.CollapsingToolbarLayout
android:id=&quot;@+id/productCollapseToolbar&quot;
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;256dp&quot;
android:fitsSystemWindows=&quot;true&quot;
app:contentScrim=&quot;?attr/colorPrimary&quot;
app:layout_scrollFlags=&quot;scroll|exitUntilCollapsed|snap&quot;
app:titleEnabled=&quot;false&quot;&gt;
&lt;ImageView
android:id=&quot;@+id/htab_header&quot;
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;match_parent&quot;
android:background=&quot;@drawable/ic_default_product&quot;
android:fitsSystemWindows=&quot;true&quot;
android:scaleType=&quot;centerCrop&quot;
app:layout_collapseMode=&quot;parallax&quot;/&gt;
&lt;View
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;match_parent&quot;
android:alpha=&quot;0.3&quot;
android:background=&quot;@android:color/black&quot;
android:fitsSystemWindows=&quot;true&quot;/&gt;
&lt;ImageButton
android:id=&quot;@+id/backBtn&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;
android:background=&quot;@color/colorPrimary&quot;
android:tint=&quot;@color/colorSecondary&quot;
android:padding=&quot;@dimen/btn_padding&quot;
android:layout_margin=&quot;@dimen/btn_padding&quot;
android:onClick=&quot;OnNavigateBack&quot;
android:src=&quot;@drawable/ic_previous&quot;/&gt;
&lt;/com.google.android.material.appbar.CollapsingToolbarLayout&gt;
&lt;/com.google.android.material.appbar.AppBarLayout&gt;
&lt;androidx.core.widget.NestedScrollView
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;
app:layout_behavior=&quot;@string/appbar_scrolling_view_behavior&quot;&gt;
&lt;LinearLayout
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;wrap_content&quot;
android:padding=&quot;@dimen/default_page_padding&quot;
android:orientation=&quot;vertical&quot;&gt;
&lt;TextView
android:id=&quot;@+id/productTitle&quot;
style=&quot;@style/ProductTitle&quot;
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;match_parent&quot;
android:layout_marginBottom=&quot;@dimen/btn_padding&quot;
android:text=&quot;Growers Mix&quot;/&gt;
&lt;TextView
android:id=&quot;@+id/productDescription&quot;
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;match_parent&quot;
android:text=&quot;Test&quot;/&gt;
&lt;/LinearLayout&gt;
&lt;/androidx.core.widget.NestedScrollView&gt;
&lt;/androidx.coordinatorlayout.widget.CoordinatorLayout&gt;
&lt;/layout&gt;

Take note on the ImageButton, I have declared the OnNavigateBack method in the HomeActivity.java because declaring it on the ProductFragment.java causes some errors saying that it can't find a method like that, and so I declare it on the HomeActivity. This is how it looks like:

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() + &quot; should be viewed in a separate fragment&quot;);
Bundle bundle = new Bundle();
bundle.putInt(&quot;productId&quot;, product.getId());
ProductFragment productFragment = new ProductFragment();
productFragment.setArguments(bundle);
navigateToFragment(productFragment);
}

It is just calling the navigateToFragment() method from before. But this time everytime I click the back button from the ProductFragment, it renders a black screen for at least 1 second and load the list of products. I'm not sure what seems to be wrong because I just load the fragment the same way as the others. Is there something I missed? Or the approach I made was wrong? What should I do? Please help. Thanks in advance.

答案1

得分: 0

找到了一个解决方案,看起来在片段的布局中使用 android:onClick 属性时,绑定功能无法正常运行。因此,我回归到了以前的方式,通过在返回按钮上设置 onClick 监听器来绑定点击事件:

做法:

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"
    ... />
...
英文:

Found a solution on this one, it seems the binding is not functioning properly using android:onClick property in the fragment's layout. So I return to the old way of binding click events by binding it by setting an onClickListener on the back button:

DO:

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

I DONT DO FROM NOW ON EVER:

fragment_product.xml

...
&lt;ImageButton
....
android:id=&quot;@+id/backBtn&quot;
android:onClick=&quot;onProductClick&quot;
... /&gt;
...

huangapple
  • 本文由 发表于 2020年10月5日 12:31:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/64202512.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定