I have a really odd issue that isn't generating any errors so its extremely hard to troubleshoot (at least for me).
My app has 3 tabs. Tab 1 has a list view inside a fragment and the other two tabs are basically empty placeholder fragments. When I click on Tab 1 and then Tab 2, then back to Tab 1, my list view remains as expected. However if I click on Tab 3 then click on Tab 1, my list view disappears. I see no difference between Tab 2 and Tab 3 that would cause this.
I've added the related code from my app. Please let me know if more is needed.
Any help and advice would be much appreciated!
My Main Activity:
var mCursorAdapter: DbCursorAdapter? = null
class CatalogActivity : AppCompatActivity(), LoaderManager.LoaderCallbacks<Cursor> {
private lateinit var mFragmentPagerAdapter: FragmentPagerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_catalog)
setSupportActionBar(toolbar)
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mFragmentPagerAdapter = FragmentPagerAdapter(supportFragmentManager)
// Set up the ViewPager with the sections adapter.
view_pager_container.adapter = mFragmentPagerAdapter
view_pager_container.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabs))
tabs.addOnTabSelectedListener(TabLayout.ViewPagerOnTabSelectedListener(view_pager_container))
// Setup FAB to open EditorActivity
fab_button.setOnClickListener {
val intent = Intent(this@CatalogActivity, EditorActivity::class.java)
startActivity(intent)
}
// Kick off the loader
loaderManager.initLoader(INGREDIENT_LOADER, null, this)
}
/**
* Helper method to insert hardcoded ingredient data into the database. For debugging purposes only.
*/
private fun insertIngredient() {
// Create a ContentValues object where column names are the keys,
// and Rittenhouse Rye Whiskey attributes are the values.
val values = ContentValues()
values.put(IngredientEntry.COLUMN_INGREDIENT_NAME, "Rittenhouse Rye")
values.put(IngredientEntry.COLUMN_INGREDIENT_DESCRIPTION, "Earthly with a sweet finish.")
values.put(IngredientEntry.COLUMN_INGREDIENT_CATEGORY, IngredientEntry.CATEGORY_WHISKEY)
values.put(IngredientEntry.COLUMN_INGREDIENT_WEIGHT, 1)
// Insert a new row for Rittenhouse Rye Whiskey into the provider using the ContentResolver.
// Use the {@link IngredientEntry#CONTENT_URI} to indicate that we want to insert
// into the ingredients database table.
// Receive the new content URI that will allow us to access Rittenhouse's data in the future.
contentResolver.insert(IngredientEntry.CONTENT_URI, values)
}
/**
* Helper method to delete all ingredients in the database.
*/
private fun deleteAllIngredients() {
val rowsDeleted = contentResolver.delete(IngredientEntry.CONTENT_URI, null, null)
Log.v("CatalogActivity", rowsDeleted.toString() + " rows deleted from ingredient database")
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu options from the res/menu/menu_catalog.xml file.
// This adds menu items to the app bar.
menuInflater.inflate(R.menu.menu_catalog, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// User clicked on a menu option in the app bar overflow menu
when (item.itemId) {
// Respond to a click on the "Insert dummy data" menu option
R.id.action_insert_dummy_data -> {
insertIngredient()
return true
}
// Respond to a click on the "Delete all entries" menu option
R.id.action_delete_all_entries -> {
deleteAllIngredients()
return true
}
}
return super.onOptionsItemSelected(item)
}
override fun onCreateLoader(i: Int, bundle: Bundle?): Loader<Cursor> {
// Define a projection that specifies the columns from the table we care about.
val projection = arrayOf(IngredientEntry._ID, IngredientEntry.COLUMN_INGREDIENT_NAME, IngredientEntry.COLUMN_INGREDIENT_DESCRIPTION)
// This loader will execute the DbContentProvider's query method on a background thread
return CursorLoader(this, // Parent activity context
IngredientEntry.CONTENT_URI, // Provider content URI to query
projection, null, null, null)// Columns to include in the resulting Cursor
// No selection clause
// No selection arguments
// Default sort order
}
override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) {
// Update {@link DbCursorAdapter} with this new cursor containing updated ingredients data
mCursorAdapter?.swapCursor(data)
}
override fun onLoaderReset(loader: Loader<Cursor>) {
// Callback called when the data needs to be deleted
mCursorAdapter?.swapCursor(null)
}
companion object {
/** Identifier for the ingredients data loader */
private val INGREDIENT_LOADER = 0
}
}
My Ingredient Fragment:
class IngredientsFragment : Fragment() {
override fun onAttach(context: Context?) {
super.onAttach(context)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_ingredient, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Set empty view on the ListView, so that it only shows when the ingredient_list_view has 0 items.
ingredient_list_view.emptyView = empty_view
// Setup an Adapter to create a ingredient_list_view item for each row of ingredient data in the Cursor.
// There is no ingredient data yet (until the loader finishes) so pass in null for the Cursor.
mCursorAdapter = DbCursorAdapter(context, null)
ingredient_list_view.adapter = mCursorAdapter
// Setup the item click listener
ingredient_list_view.onItemClickListener = AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, id: Long ->
// Create new intent to go to {@link EditorActivity}
val intent = Intent(context, EditorActivity::class.java)
// Form the content URI that represents the specific ingredient that was clicked on,
// by appending the "id" (passed as input to this method) onto the
// {@link IngredientEntry#CONTENT_URI}.
// For example, the URI would be "content://onCreateDesigns.com.CraftCocktailRecipes.ingredients/ingredients/2"
// if the ingredient with ID 2 was clicked on.
val currentUri = ContentUris.withAppendedId(DbContract.IngredientEntry.CONTENT_URI, id)
// Set the URI on the data field of the intent
intent.data = currentUri
// Launch the {@link EditorActivity} to display the data for the current ingredient.
startActivity(intent)
}
}
override fun onDetach() {
super.onDetach()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
}
}
My Adapter:
class FragmentPagerAdapter(fm: FragmentManager) : SmartFragmentStatePagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
return when (position) {
0 -> IngredientsFragment()
1 -> RecipesFragment()
else -> FavoritesFragment()
}
}
override fun getCount(): Int {
// Show 3 total pages.
return 3
}
}
My Ingredient Fragment:
<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="onCreateDesigns.com.CraftCocktailRecipes.IngredientsFragment">
<ListView
android:id="@+id/ingredient_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!-- Empty view for ingredient_list_view list -->
<RelativeLayout
android:id="@+id/empty_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<ImageView
android:id="@+id/empty_shelter_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:src="@drawable/ic_empty_shelter"
android:contentDescription="@string/empty_shelter" />
<TextView
android:id="@+id/empty_title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/empty_shelter_image"
android:layout_centerHorizontal="true"
android:fontFamily="sans-serif-medium"
android:paddingTop="16dp"
android:text="@string/empty_view_title_text"
android:textAppearance="?android:textAppearanceMedium"/>
<TextView
android:id="@+id/empty_subtitle_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/empty_title_text"
android:layout_centerHorizontal="true"
android:fontFamily="sans-serif"
android:paddingTop="8dp"
android:text="@string/empty_view_subtitle_text_ingredients"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="#A2AAB0"/>
</RelativeLayout>
I think it's the default functionality of a view-pager that at that time of set Adapter view-pager load Two Fragments, when you move fragment 1 to 3 then it will release fragment 1 and wise versa,
if you want to load all of the fragment at the first time than write one line after setAdapter
view_pager_container.setOffscreenPageLimit(3);
setOffscreenPageLimit() this method load all your three fragments at that time or never initialized again.
Might be helpfull.