0
When I'm building my app, the image loading is very laggy. It seems to happen in conjuction with adding a BottomNavigationView or Toolbar, when they are removed app runs smoothly. Setup is a MainActivity and fragments loaded into it thru bottom nav bar.
public class FragmentActivity extends AppCompatActivity {
BottomNavigationView navi;
FragmentTransaction fragmentTransaction;
public static final String TAG = FragmentActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_activity);
if (findViewById(R.id.fragment_container) != null) {
if (savedInstanceState != null) {
return;
}
// Create a new Fragment to be placed in the activity layout
SearchFragment searchFragment = new SearchFragment();
//currentFragment = new RewardsFragment();
// In case this activity was started with special instructions from an
// Intent, pass the Intent's extras to the fragment as arguments
// Add the fragment to the 'fragment_container' FrameLayout
//getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, currentFragment).commit();
//setBottomNavBar();
}
navi = findViewById(R.id.navigation);
Bundle intent = getIntent().getExtras();
if(intent != null) {
String venueID = intent.getString("venue_id");
String venueName = intent.getString("venue_name");
VenueFragment venueFragment = new VenueFragment();
Bundle args = new Bundle();
args.putString("venue_id", venueID);
args.putString("venue_name", venueName);
venueFragment.setArguments(args);
switchFragment(venueFragment);
} else {
switchFragment(new SearchFragment());
}
unCheckAllMenuItems(navi.getMenu());
navi.setOnNavigationItemSelectedListener(item -> {
Fragment fragment;
if (item.getItemId() == R.id.search) {
item.setChecked(false);
fragment = new SearchFragment();
switchFragment(fragment);
return false;
} else if (item.getItemId() == R.id.rewards) {
item.setChecked(false);
fragment = new RewardsFragment();
switchFragment(fragment);
return false;
}
return false;
});
}
private void switchFragment(Fragment fragment) {
fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
xml layout
<android.support.constraint.ConstraintLayout
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:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/navigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#color/white"
app:itemBackground="#color/white"
app:itemIconTint="#color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/navigation_bar_menu" />
GridView gridview = getView().findViewById(R.id.postsGridTest);
Log.v(TAG, "running");
//declare adapter
if (adapter != null) {
adapter.setImages(image_list);
//adapter.notifyDataSetChanged();
waitingPosts = false; //reset this var now that we have loaded in more posts
image_list = new ArrayList<String>();
return;
}
adapter = new PostAdapter(getContext(), image_list);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
gridview.setAdapter(adapter);
}
});
^code for loading in an image into gridview, though the issue occurs when loading a single image into a layout as well, in a different fragment
Related
I have one MainActivity and two fragments FGames and FGameDetail. I am trying to communicate between them so that when an item is clicked in the FGames it passes GameEntity to MainActivity and it updates the FGameDetail. I have separate layout created for phone and tablet.
In my MainActivity where I am controlling if the FGameDetail fragment exists in the layout update the view. but findFragmentById always returns null for both FGames and FGameDetails.
Here is my layout for Phones and Tablets
layout\activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/games_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.rao.igttest.MainActivity">
<fragment
android:id="#+id/fragment_fGames"
android:name="com.example.rao.igttest.Games.View.FGames"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
layout-large-land\activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/fragment_fGames"
android:name="com.example.rao.igttest.Games.View.FGames"
android:layout_width="400dp"
android:layout_height="wrap_content" />
<fragment
android:id="#+id/fragment_fGameDetail"
android:name="com.example.rao.igttest.Games.View.FGameDetail"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity implements FGames.OnGameSelectedListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//FGames fGames = new FGames();
//getSupportFragmentManager().beginTransaction().add(R.id.games_container, fGames).commit();
}
#Override
public void onGameSelected(GameEntity gameEntity) {
FragmentManager fragmentManager = getSupportFragmentManager();
FGameDetail gameDetailFrag = (FGameDetail) fragmentManager
.findFragmentById(R.id.fragment_fGameDetail);
Fragment gameFrag = fragmentManager.findFragmentById(R.id.fragment_fGames);
if (gameDetailFrag == null) {
//TODO Open a new intent
} else {
// DisplayFragment (Fragment B) is in the layout (tablet layout),
// so tell the fragment to update
FGameDetail fGameDetail = new FGameDetail();
fGameDetail.updateContent(gameEntity);
}
}
}
Your help and suggestions is much appreciated.
EDIT
FGAMES
#Override
public void initRecyclerView(List<GameEntity> gameEntities) {
gamesAdapter = new GamesAdapter(getActivity(), gameEntities);
rvGames.setAdapter(gamesAdapter);
rvGames.setLayoutManager(new LinearLayoutManager(getActivity()));
}
GamesAdapter
//Constructor
public GamesAdapter(Context context, List<GameEntity> gameEntities) {
layoutInflater = LayoutInflater.from(context);
this.data = gameEntities;
this.context = context;
}
#OnClick(R.id.row_container)
void rowClick(){
//I think as I am creating a new instance here as I dont have referencec to GamesPresenter in adapter as I am calling it directly from FGames
GamesPresenter gamesPresenter = new GamesPresenterImpl();
gamesPresenter.showGameDetail(data.get(getLayoutPosition()));
Toast.makeText(context, "itemClicked " + data.get(getLayoutPosition()), Toast.LENGTH_SHORT).show();
}
GamesPresenterImpl
#Override
public void showGameDetail(GameEntity gameEntity) {
GamesView gamesView = new FGames();
gamesView.onListItemClick(gameEntity);
}
FGames
#Override
public void onListItemClick(GameEntity gameEntity) {
OnGameSelectedListener mListener = new MainActivity();
mListener.onGameSelected(gameEntity);
}
public interface OnGameSelectedListener{
public void onGameSelected(GameEntity gameEntity);
}
MainActivity - OnGameSelected.
#Override
public void onGameSelected(GameEntity gameEntity) {
FragmentManager fragmentManager = getSupportFragmentManager();
FGameDetail gameDetailFrag = (FGameDetail) fragmentManager
.findFragmentById(R.id.fragment_fGameDetail);
Fragment gameFrag = fragmentManager.findFragmentById(R.id.fragment_fGames);
if (gameDetailFrag == null) {
} else {
// DisplayFragment (Fragment B) is in the layout (tablet layout),
// so tell the fragment to update
FGameDetail fGameDetail = new FGameDetail();
fGameDetail.updateContent(gameEntity);
}
}
Are you sure you are not doing new MainActivity().onGameSelected(object)?
If you using the Interactor fragment pattern, you must use the instance of the Activity you already have in the class.
Also, this line dont update the screen cause you creating a new instance of the fragment that will never be added:
FGameDetail fGameDetail = new FGameDetail();
fGameDetail.updateContent(gameEntity);
There is a instance already in screen so:
gameDetailFrag.updateContent(gameEntity);
I'm using HashMap of fragment's backstack. To save backstack and current fragment I use the code below:
public class MainActivity extends AppCompatActivity {
private HashMap<String, Stack<Fragment>> mStacks;
public static final String TAB_PROFILE = "tab_profile";
public static final String TAB_DASHBOARD = "tab_dashboard";
public static final String TAB_CHATS = "tab_chats";
public static final String TAB_SETTINGS = "tab_settings";
private String mCurrentTab;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupViews();
if (savedInstanceState != null) {
mCurrentTab = savedInstanceState.getString("currentTab");
mStacks = (HashMap<String, Stack<Fragment>>) savedInstanceState.getSerializable("stacks");
} else
selectedTab(TAB_DASHBOARD);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("stacks", mStacks);
outState.putString("currentTab", mCurrentTab);
}
private void setupViews() {
mStacks = new HashMap<>();
mStacks.put(TAB_PROFILE, new Stack<>());
mStacks.put(TAB_DASHBOARD, new Stack<>());
mStacks.put(TAB_CHATS, new Stack<>());
mStacks.put(TAB_SETTINGS, new Stack<>());
BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
bottomNavigationView.setSelectedItemId(R.id.action_dashboard);
BottomNavigationViewHelper.removeShiftMode(bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
switch (item.getItemId()) {
case R.id.action_profile:
selectedTab(TAB_PROFILE);
return true;
case R.id.action_dashboard:
selectedTab(TAB_DASHBOARD);
return true;
case R.id.action_chats:
selectedTab(TAB_CHATS);
return true;
case R.id.action_settings:
selectedTab(TAB_SETTINGS);
return true;
}
return true;
});
bottomNavigationView.setOnNavigationItemReselectedListener(item -> {
if (mStacks.get(mCurrentTab).size() != 1) {
mStacks.get(mCurrentTab).clear();
switch (item.getItemId()) {
case R.id.action_profile:
selectedTab(TAB_PROFILE);
break;
case R.id.action_dashboard:
selectedTab(TAB_DASHBOARD);
break;
case R.id.action_chats:
selectedTab(TAB_CHATS);
break;
case R.id.action_settings:
selectedTab(TAB_SETTINGS);
break;
}
}
});
}
private void selectedTab(String tabId) {
mCurrentTab = tabId;
if(mStacks.get(tabId).size() == 0){
if(tabId.equals(TAB_PROFILE)){
Fragment fragment = new ProfileFragment();
Bundle args = new Bundle();
args.putSerializable("user", Globals.getCurrentUser());
fragment.setArguments(args);
pushFragments(tabId, fragment,true);
} else if(tabId.equals(TAB_DASHBOARD)){
pushFragments(tabId, new DashboardFragment(),true);
}else if(tabId.equals(TAB_CHATS)){
pushFragments(tabId, new GroupsFragment(),true);
}else if(tabId.equals(TAB_SETTINGS)){
pushFragments(tabId, new SettingsFragment(),true);
}
}else {
pushFragments(tabId, mStacks.get(tabId).lastElement(),false);
}
}
public void pushFragments(String tag, Fragment fragment, boolean shouldAdd){
if(shouldAdd)
mStacks.get(tag).push(fragment);
FragmentManager manager = getFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
ft.commit();
}
public void popFragments(){
Fragment fragment = mStacks.get(mCurrentTab).elementAt(mStacks.get(mCurrentTab).size() - 2);
mStacks.get(mCurrentTab).pop();
FragmentManager manager = getFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
ft.commit();
}
#Override
public void onBackPressed() {
if(mStacks.get(mCurrentTab).size() == 1){
finish();
return;
}
popFragments();
}
}
Set new fragments using
((MainActivity)context).pushFragments(MainActivity.TAB_CHATS, fragment,true);
Layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="#color/background_material_light"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/bottom_navigation"/>
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:itemBackground="#color/waPrimary"
app:itemIconTint="#color/white"
app:itemTextColor="#color/white"
app:menu="#menu/menu_bottom_navigation" />
Everything works fine on screen rotation, but application crashes with exception on application hide.
java.lang.RuntimeException: Parcel: unable to marshal value %FragmentName%{c985244 #2 id=0x7f090051}
As I read, it happens when one of the objects I'm trying to pass is not Parceable, but have no idea how to fix this. Any thoughts?
UPD
After I made all of my fragments Serializable, new exception throws
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = %FragmentName%)
...
Caused by: java.io.NotSerializableException: android.support.v7.widget.RecyclerView
UPD2
Seems like a found a solution - transient property. Now I'm trying to make all non-serializeable objects transient.
UPD3
It helped, but I don't know is it efficient enough.
Here's my suggestion:
Your activity maintains a reference to the four fragments it wants for the bottom navigation toggling.
On toggling bottom navigation, you replace the current fragment in the activity fragment manager.
While on a given fragment, as you interact with the UI, you push things on to the fragment child fragment manager.
This way, each fragment maintains its own backstack automatically, you don't have to save any state, and it all Just Works™.
Some sample code that might help.
public class MainActivity extends AppCompatActivity {
private Fragment mProfileFragment;
private Fragment mDashboardFragment;
private Fragment mChatsFragment;
private Fragment mSettingsFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
// Init fragments
}
else {
// Find last active fragments in fragment manager
}
setupViews();
}
private void setupViews() {
BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setSelectedItemId(R.id.action_dashboard);
BottomNavigationViewHelper.removeShiftMode(bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
Fragment fragment;
switch (item.getItemId()) {
case R.id.action_profile:
fragment = mProfileFragment;
break;
case R.id.action_dashboard:
fragment = mDashboardFragment;
break;
case R.id.action_chats:
fragment = mChatsFragment;
break;
case R.id.action_settings:
fragment = mSettingsFragment;
break;
}
// Replace the currently active fragment which will be
// managing its own backstack
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frament_container, fragment)
.commit();
});
}
}
And one of your fragments would push stuff on its own stack like this:
public class ProfileFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.id.fragment_layout, container, false);
Button button = view.findViewById(R.id.some_button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Fragment someFragmentToPush = new SomeFragmentToPush();
// Use the child fragment manager to keep UI
// local to this fragment instance, adding to backstack
// for automatic popping on pressing back
getChildFragmentManager().beginTransaction()
.add(R.id.fragment_layout, someFragmentToPush)
.addToBackStack(null)
.commit();
}
});
return view;
}
}
Hope that helps!
I have used BottomNavigationView in my app, but the text of menu item overlaps on menu icon in small devices as shown in below screenshot.
Edit:
Here is the java code:
public class MainActivity extends AppCompatActivity {
private static final String SELECTED_ITEM = "arg_selected_item";
private BottomNavigationView mBottomNav;
private int mSelectedItem;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBottomNav = (BottomNavigationView) findViewById(R.id.navigation);
BottomNavigationViewHelper.disableShiftMode(mBottomNav);
mBottomNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
selectFragment(item);
return true;
}
});
MenuItem selectedItem;
if (savedInstanceState != null) {
mSelectedItem = savedInstanceState.getInt(SELECTED_ITEM, 0);
selectedItem = mBottomNav.getMenu().findItem(mSelectedItem);
} else {
selectedItem = mBottomNav.getMenu().getItem(0);
}
selectFragment(selectedItem);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(SELECTED_ITEM, mSelectedItem);
super.onSaveInstanceState(outState);
}
#Override
public void onBackPressed() {
MenuItem homeItem = mBottomNav.getMenu().getItem(0);
if (mSelectedItem != homeItem.getItemId()) {
// select home item
selectFragment(homeItem);
} else {
super.onBackPressed();
}
}
private void selectFragment(MenuItem item) {
// init corresponding fragment
switch (item.getItemId()) {
case R.id.menu_explore:
pushFragment(new ExploreFragment());
break;
case R.id.menu_how_it_works:
pushFragment(new HowItWorksFragment());
break;
case R.id.menu_contact_us:
pushFragment(new ContactUsFragment());
break;
case R.id.menu_profile:
pushFragment(new MyProfileFragment());
break;
}
// update selected item
mSelectedItem = item.getItemId();
// uncheck the other items.
for (int i = 0; i < mBottomNav.getMenu().size(); i++) {
MenuItem menuItem = mBottomNav.getMenu().getItem(i);
menuItem.setChecked(menuItem.getItemId() == item.getItemId());
}
updateToolbarText(item.getTitle());
}
protected void pushFragment(Fragment fragment) {
if (fragment == null)
return;
FragmentManager fragmentManager = getFragmentManager();
if (fragmentManager != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (ft != null) {
ft.replace(R.id.container, fragment);
ft.commit();
}
}
}
private void updateToolbarText(CharSequence text) {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(text);
}
}
}
Here is the xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:design="http://schemas.android.com/apk/res-auto"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:background="#android:color/white"
design:menu="#menu/bottom_nav_items" />
</LinearLayout>
How to resolve this issue ?
Please Help!!
Do not use long texts on bottom bar. It's out of guideline. Bottom Bar is not developed to use like that.
Text labels provide short, meaningfully definitions to bottom
navigation icons. Avoid long text labels as these labels do not
truncate or wrap.
Read all documentation and usage descriptions at this Source.
i have encountred this problem and performed folowing solution to fix it but couldnt fix this problem yet. And also i tried to use fragmentpageradapter instead of fragmentstatepageradapterbut still facing same problem.
java.lang.IllegalStateException: Fragement no longer exists for key f1: index 3
this crash happens when i go back to fragment which contains viewpager.
Any idea?
firstly pushing productdetail fragment to "maincontainer"
public void switchToProductDetail(SearchHelper searchHelper, Product product)
{
ProductDetailFragment productDetailFragment = new ProductDetailFragment();
productDetailFragment.product = product;
productDetailFragment.searchHelper = searchHelper;
setNewFragment(productDetailFragment, R.id.mainContainer, "ProductDetail", true, true, true, true);
}
and then if user doesnt sign in and pressed to add to fav button, i am pushing signup fragment to "window" container.
public void switchtoSignIn(SignInSignUpFragment.SignInCompleteCallBack callBack)
{
SignInSignUpFragment signInSignUpFragment = new SignInSignUpFragment();
signInSignUpFragment.callBack = callBack;
setNewFragment(signInSignUpFragment, R.id.windowFL, "SignIn", true, true, true, true);
}
And the mainactivity xml(which contains the maincontainer and windowFL)
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/windowFL"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="#+id/headerContainer" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mainContainer"/>
</LinearLayout>
<LinearLayout
android:id="#+id/left_menu_frame"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_marginRight="#dimen/menumargin"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#ffffff" />
</android.support.v4.widget.DrawerLayout>
setnewfragment method is:
#SuppressLint("CommitTransaction")
public void setNewFragment(final Fragment fragment, final int containerId, final String tag, final boolean shouldAddBackStack, final boolean shouldReplace, final boolean shouldAnimation, final boolean shouldDrawerClose) {
if(isAnyMenuOpen() && shouldDrawerClose)
{
drawerLayout.closeDrawer(GravityCompat.START, true);
drawerClosedCallBack = new DrawerClosedCallBack() {
#Override
public void onDrawerClosed() {
setNewFragment(fragment, containerId, tag, shouldAddBackStack, shouldReplace, shouldAnimation, false);
}
};
return;
}
if (fragmentManager == null)
{
fragmentManager = getSupportFragmentManager();
}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if (shouldAnimation)
{
if(fragment instanceof SignInSignUpFragment)
{
fragmentTransaction.setCustomAnimations(R.anim.slide_in_bottom, R.anim.fade_out, R.anim.fade_in, R.anim.slide_out_bottom);
}
else
{
fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right);
}
}
if (shouldReplace)
{
fragmentTransaction.replace(containerId, fragment);
}
else
{
fragmentTransaction.add(containerId, fragment);
}
if(shouldAddBackStack)
{
fragmentTransaction.addToBackStack(tag);
}
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.commitAllowingStateLoss();
}
EDIT: The problem below occurs on my Samsung Galaxy S3, however, when I run the same app on my Sony Xperia Z3+ it doesn't display the WIFI list at all. :-/
I have a weird situation with my app. I currently have five different fragments. All of them work as expected when doing FragmentTransactions except for one.
Initially, when I started my app I used one of the Android Studio templates, but this seemed like serious overkill as it used Fragments instead of a ListView for listing my WIFI items. It's been a while since I've developed anything with Android, so I'm playing catch-up.
I left the code in place and carried on developing the interface. The trouble came along when I eventually decided to remove the code that populated the main container with Fragment "items" and replace it with a Fragment containing a ListView.
All my Fragments work as expected and are replaced as expected when I select a new item from the menu except this new ListView Fragment, which remains in the background once I select it.
After I select it, if I select other Fragments they change as they should, but this first one stays in place.
The closest question I've found to my situation is this one, but it didn't help me.
This is what my screen looks like after selecting the problem Fragment and another Fragment:
My MainActivity onCreate() method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setupWifiScan();
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Scanning for devices...", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
scanForNetworks(view);
}
});
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
if(findViewById(R.id.fragment_container) != null){
if(savedInstanceState != null){
return;
}
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
WifiFragment wifiFragment = new WifiFragment();
wifiFragment.setArguments(getIntent().getExtras());
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_container, wifiFragment);
transaction.commit();
}else{
Log.i("MAIN_ACTIVITY", "fragment_container is NULL");
}
}
My MainActivity onNavigationItemSelected() method:
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_settings) {
GeneralSettings generalSettings = new GeneralSettings();
Bundle args = new Bundle();
args.putInt(GeneralSettings.ARG_POSITON, 0);
generalSettings.setArguments(args);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_container, generalSettings);
transaction.addToBackStack(null);
int stackId = transaction.commit();
Log.i(this.getClass().getSimpleName(), "Stack ID: " + stackId);
}else if(id == R.id.nav_wlan_setting){
WifiSettings wifiSettings = new WifiSettings();
Bundle args = new Bundle();
args.putInt(GeneralSettings.ARG_POSITON, 0);
wifiSettings.setArguments(args);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_container, wifiSettings);
transaction.addToBackStack(null);
int stackId = transaction.commit();
Log.i(this.getClass().getSimpleName(), "Stack ID: " + stackId);
}else if(id == R.id.nav_led_settings){
UvLedSettings uvLedSettings = new UvLedSettings();
Bundle args = new Bundle();
args.putInt(GeneralSettings.ARG_POSITON,0);
uvLedSettings.setArguments(args);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_container, uvLedSettings);
transaction.addToBackStack(null);
int stackId = transaction.commit();
Log.i(this.getClass().getSimpleName(), "Stack ID: " + stackId);
}else if(id == R.id.nav_server_settings){
ServerSettings serverSettings = new ServerSettings();
Bundle args = new Bundle();
// TODO: Fix Args Settings for all Fragments
args.putInt(GeneralSettings.ARG_POSITON, 0);
serverSettings.setArguments(args);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_container, serverSettings);
transaction.addToBackStack(null);
int stackId = transaction.commit();
Log.i(this.getClass().getSimpleName(), "Stack ID: " + stackId);
} else if (id == R.id.nav_current_device) {
} else if (id == R.id.nav_available_devices){
WifiFragment wifi = new WifiFragment();
Bundle args = new Bundle();
args.putInt(GeneralSettings.ARG_POSITON, 0);
wifi.setArguments(args);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_container, wifi);
transaction.addToBackStack(null);
int stackId = transaction.commit();
Log.i(this.getClass().getSimpleName(), "Stack ID: " + stackId);
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
The problem Fragment:
package com.myapp.serviceapplication.fragments;
import android.content.Context;
import android.net.wifi.ScanResult;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.RelativeLayout;
import com.myapp.serviceapplication.R;
import com.myapp.serviceapplication.adapters.WifiItemListAdapter;
import java.util.ArrayList;
/**
* A fragment representing a list of Items.
* <p>
* Activities containing this fragment MUST implement the {#link OnListFragmentInteractionListener}
* interface.
*/
public class WifiFragment extends Fragment {
// TODO: Customize parameter argument names
private static final String ARG_COLUMN_COUNT = "column-count";
// TODO: Customize parameters
private int mColumnCount = 1;
private OnListFragmentInteractionListener mListener;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public WifiFragment() {
// Required empty public constructor
}
// TODO: Customize parameter initialization
#SuppressWarnings("unused")
public static WifiFragment newInstance(int columnCount) {
WifiFragment fragment = new WifiFragment();
Bundle args = new Bundle();
args.putInt(ARG_COLUMN_COUNT, columnCount);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_wifi_list, container, false);
if(view instanceof RelativeLayout){
Context context = view.getContext();
RelativeLayout relativeLayout = (RelativeLayout) view;
ListView listView = (ListView) relativeLayout.findViewById(R.id.lst_wifi_items);
listView.setAdapter(new WifiItemListAdapter(this.getContext(), new ArrayList<ScanResult>()));
}
return view;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
// TODO: This needs to be modified to the correct listener type
if (context instanceof OnListFragmentInteractionListener) {
mListener = (OnListFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnListFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnListFragmentInteractionListener {
// TODO: Update argument type and name
void onListFragmentInteraction(View item);
}
}
The problem Fragment layout file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".fragments.WifiFragment"
>
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/lst_wifi_items"
android:background="#ffffff"/>
</RelativeLayout>
The content_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".activities.MainActivity"
tools:showIn="#layout/app_bar_main">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
The activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout 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:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
</android.support.v4.widget.DrawerLayout>
I've spent too many hours trying to figure this out, but I just can't see the issue.
That's happen because your fragment container is above the listview and by default if you not use background in a ViewGroup(LinearLayout, RelativeLayout,...) then that's gonna be transparent, so, all you need do is put a background in your fragment container:
fragment_wifi_list.xml
<RelativeLayout 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=".fragments.WifiFragment"
android:background="#ffffff"
>
...
TIP:
android:clickable="true"
That's avoid concurrence clicks problems
Add the following lines to the top relative layout of problematic fragment
android:background="#ffffff"
android:clickable="true"
Hope it works.