search in a recyclerview - android

i have a fragment that have a recyclerview i'm trying to use setOnQueryTextListener to search some specific data and display the new data instead of the old one
this is my Fragmenttask.kt file
class Fragmenttask : Fragment(), SwipeRefreshLayout.OnRefreshListener {
private var mListener: OnFragmentInteractionListener? = null
private var mSwipeRefreshLayout: SwipeRefreshLayout? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
//container!!.removeAllViews()
setHasOptionsMenu(true)
val CustumViewtask = inflater!!.inflate(R.layout.fragment_fragmenttask, container, false)
val taskMainRV = CustumViewtask.findViewById(R.id.recyclerView_main) as RecyclerView
//taskMainRV.setBackgroundColor(Color.BLUE)
taskMainRV.layoutManager = LinearLayoutManager(context)
//recyclerView_main.adapter = MainAdapter()
fetchJson(taskMainRV)
return CustumViewtask
}
override fun onRefresh() {
Toast.makeText(context, "تم التحميل بنجاح والحمد لله", Toast.LENGTH_LONG).show()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_user, menu)
val searchItem = menu.findItem(R.id.action_search)
val searchView = MenuItemCompat.getActionView(searchItem) as SearchView
searchView.queryHint = "أكتب كلمة بحث ..."
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextChange(q: String): Boolean {
println("my serch : $q")
//fetchJson(taskMainRV)
//adaptertask?.notifyDataSetChanged()
//notifyDataSetChanged
println("mohamed 78797987897987987987987987987987987987897")
return false
}
override fun onQueryTextSubmit(q: String): Boolean {
println("my serch : $q")
println("mohamed 78797987897987987987987987987987987987897**************")
return false
}
})
//searchView.setOnQueryTextListener(this)
//https://stackoverflow.com/questions/30398247/how-to-filter-a-recyclerview-with-a-searchview?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
}
fun onQueryTextChange(query: String): Boolean {
// Here is where we are going to implement the filter logic
return false
}
fun onQueryTextSubmit(query: String): Boolean {
return false
}
fun fetchJson(RSV: RecyclerView) {
//SharedPreferences
val MY_APP_INFO: String = "UserInfo"
val prefs = activity.getSharedPreferences(MY_APP_INFO, AppCompatActivity.MODE_PRIVATE)
val LoggedUserId = prefs.getString("UserId", null)
var serchqury = arguments.getString("serchqury")
if(serchqury.isNullOrEmpty()){
serchqury = ""
}
println(serchqury+" ------------**/*/*/*/*/*/*/*/*/*/ ")
println("your code is : $LoggedUserId")
println("Attempting to Fetch JSON")
val url = "http://www.deraah-rs.com/1mahamatonline/html/Restful/get_tasks.php"
val client = OkHttpClient()
val formBody = FormBody.Builder().add("UserId", LoggedUserId)
.add("serchqury", serchqury).build()
val request = Request.Builder().url(url)
.post(formBody)
.build()
client.newCall(request).enqueue(object: Callback {
override fun onResponse(call: Call?, response: Response?) {
val body = response?.body()?.string()
println("mohamed : $body")
val gson = GsonBuilder().create()
val tasksfeed = gson.fromJson(body, M_tasksFeed::class.java)
activity.runOnUiThread {
RSV.adapter = MainAdaptertasks(tasksfeed)
if(taskfragmentprogressbar != null){
taskfragmentprogressbar.visibility = View.INVISIBLE
}
//ChatAdapter(Context, tasksfeed)
}
}
override fun onFailure(call: Call?, e: IOException?) {
println("Failed to execute request")
}
})
}
// TODO: Rename method, update argument and hook method into UI event
fun onButtonPressed(uri: Uri) {
if (mListener != null) {
mListener!!.onFragmentInteraction(uri)
}
}
companion object {
fun newInstance(): Fragmenttask {
val fragment = Fragmenttask()
val args = Bundle()
fragment.arguments = args
return fragment
}
}
}// Required empty public constructor
......................................................................................................................................

Related

RecyclerView doesn't appear in a Fragment

Why doesn't RecyclerView appear in my fragment? I've added recyclerview adapter in the fragment, but it still didn't appear. Here are the codes:
FollowersFragment.kt
class FollowersFragment : Fragment() {
private lateinit var binding: FragmentFollowersBinding
companion object {
private const val TAG = "FollowersFragment"
const val ARG_NAME = "userName"
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_followers, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentFollowersBinding.inflate(layoutInflater)
val username = arguments?.getString(ARG_NAME)
val layoutManager = LinearLayoutManager(requireActivity())
binding.rvFollowers.layoutManager = layoutManager
val itemDecoration = DividerItemDecoration(requireActivity(), layoutManager.orientation)
binding.rvFollowers.addItemDecoration(itemDecoration)
val client = ApiConfig.getApiService().getFollowers(username.toString(),"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")
client.enqueue(object : Callback<List<FollowsResponseItem>> {
override fun onResponse(
call: Call<List<FollowsResponseItem>>,
response: Response<List<FollowsResponseItem>>
) {
if(response.isSuccessful){
val responseBody = response.body()
if(responseBody!=null){
Log.d(TAG,responseBody.toString())
setUserData(responseBody)
}else{
Log.e(TAG,"onFailure: ${response.message()}")
}
}
}
override fun onFailure(call: Call<List<FollowsResponseItem>>, t: Throwable) {
Log.e(TAG, "onFailure: ${t.message}")
}
})
}
fun setUserData(item: List<FollowsResponseItem>){
val listUser = ArrayList<UserResponse>()
val executor = Executors.newSingleThreadExecutor()
executor.execute {
try {
for (i in 0..item.size-1) {
if(item.size>5 && i>5){
break
}
val client = ApiConfig.getApiService()
.getUser(item.get(i).login, "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")
client.enqueue(object : Callback<UserResponse> {
override fun onResponse(
call: Call<UserResponse>,
response: Response<UserResponse>
) {
if (response.isSuccessful) {
val responseBody = response.body()
if (responseBody != null) {
listUser.add(responseBody)
if(i==4 || item.get(i).login.equals(item.get(item.size-1).login)){
Log.d(TAG,"user : $listUser")
val adapter = ListUserAdapter(listUser)
binding.rvFollowers.adapter = adapter
Log.d(TAG,adapter.toString())
adapter.setOnItemClickCallback(object: ListUserAdapter.OnItemClickCallback{
override fun onItemClicked(data: UserParcelable) {
showSelectedUser(data)
}
})
}
} else {
Log.e(TAG, "onFailure: ${response.message()}")
}
}
}
override fun onFailure(call: Call<UserResponse>, t: Throwable) {
Log.e(TAG, "onFailure: ${t.message}")
}
})
}
} catch(e: InterruptedException) {
e.printStackTrace()
}
}
}
private fun showSelectedUser(data: UserParcelable) {
}
}
DetailActivity.kt
class DetailActivity : AppCompatActivity() {
private lateinit var binding: ActivityDetailBinding
private var getUserName: String ="sidiqpermana"
companion object{
const val EXTRA_DATA = "extra_data"
#StringRes
private val TAB_TITLES = intArrayOf(
R.string.tab_text_1,
R.string.tab_text_2
)
}
#SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportActionBar?.hide()
binding = ActivityDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
val data = intent.getParcelableExtra<UserParcelable>(EXTRA_DATA) as UserParcelable
val sectionsPagerAdapter = SectionsPagerAdapter(this)
val viewPager: ViewPager2 = binding.viewPager
viewPager.adapter = sectionsPagerAdapter
val tabs: TabLayout = binding.tabs
sectionsPagerAdapter.userName = data.login
TabLayoutMediator(tabs,viewPager){ tab, position ->
tab.text = resources.getString(TAB_TITLES[position])
}.attach()
getUserName = data.login
showInfo(data)
}
fun getUserName() : String{
return getUserName
}
#SuppressLint("SetTextI18n")
private fun showInfo(data: UserParcelable){
Glide.with(this#DetailActivity)
.load(data.avatar_url)
.into(binding.detailPp)
if(data.name.equals("null")) binding.detailName.setText("No Name") else binding.detailName.setText(data.name)
binding.detailUsername.setText(data.login)
if(data.bio.equals("null")) binding.detailBio.setText("No Name") else binding.detailBio.setText(data.bio)
binding.detailFollowers.setText("${data.followers} Followers")
binding.detailFollowings.setText("${data.following} Following")
if(data.location.equals("null")) binding.detailLocation.setText("No Location") else binding.detailLocation.setText(data.location)
}
}
ListUserAdapter.kt (RecyclerView Adapter)
class ListUserAdapter (private val listUser: ArrayList<UserResponse>) : RecyclerView.Adapter<ListUserAdapter.ListViewHolder>() {
private lateinit var onItemClickCallback: OnItemClickCallback
fun setOnItemClickCallback(onItemClickCallback: OnItemClickCallback){
this.onItemClickCallback = onItemClickCallback
}
class ListViewHolder(var binding: ItemUsersRowBinding) : RecyclerView.ViewHolder(binding.root) {
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ListViewHolder {
val binding = ItemUsersRowBinding.inflate(LayoutInflater.from(viewGroup.context),viewGroup,false)
return ListViewHolder(binding)
}
#SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
var (followers,avatar_url,following, name,bio, location, login) = listUser[position]
name = name ?: "No Name"
bio = bio ?: "No Bio"
location = location?: "No Location"
holder.apply {
Glide.with(itemView.getContext())
.load(avatar_url)
.into(binding.photoProfile)
binding.profileName.setText(name.toString())
binding.username.setText(login)
binding.followers.setText("$following Followers")
binding.followings.setText("$following Followings")
binding.location.setText(location.toString())
val detailUser = UserParcelable(followers,avatar_url,following,
name.toString(), bio.toString(), location.toString(), login)
itemView.setOnClickListener{ onItemClickCallback.onItemClicked(detailUser)}
}
}
override fun getItemCount(): Int {
return listUser.size
}
interface OnItemClickCallback {
fun onItemClicked(data: UserParcelable)
}
}
Help me solve this problem please.
There is no need to initialize adapter every time you want to update the list. Either make your ListUserAdapter extend ListAdapter and than use adapter.submitList(listUser) or if you want to extend RecyclerView.Adapter as you do, you can do the following :
class ListUserAdapter () : RecyclerView.Adapter<ListUserAdapter.ListViewHolder>() {
private val listUser: List<UserResponse>
fun submitList(newList: List<UserResponse>) {
listUser = newList
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
val listItem = listUser[position]
...
}
override fun getItemCount(): Int {
return listUser.size
}
}
I suggest you go with ListAdapter. Check if Log.d(TAG,"user : $listUser") is printed, if it is and listUser is not empty than call adapter.submitList(listUser) and RV should be populated.
You have missed to notify adapter about the changes, So after
binding.rvFollowers.adapter = adapter call adapter.notifyDataSetChanged()

Clicking on Recyclerview item has a late item

I have an application using databinding, livedata, room, kotlin koroutines, viewmodel, retrofit and koin. I have one activity, and two fragments.
UserListFragment: Show in a recyclerview a list of user items.
UserFullProfileFragment: Show the user item detail.
When the application is running, an external API is called to retrieve a list of users and display it in a recyclerview. Then, if I click on one item, an external API is called to get the detail of the current user whith its ID.
The problem is when I click on one item at the first, everything is going well but for following items, this is the detail of previous item which is displayed and so on and so forth.
Any ideas ?
UserRepository:
class UserRepositoryImpl (private val userApi: UserApi, private val userDao: UserDao, private val networkStateManager: NetworkStateManager) : UserRepository {
override suspend fun getUserList(): Result<List<UserListItem>> {
if (networkStateManager.hasNetWorkConnection()) {
return try {
// get user list from user API
val response = userApi.getUserList()
if (response.isSuccessful) {
Log.d("REPO", "get users from api")
response.body()?.let { userResponse ->
Log.d("REPO", "response:$response")
val userList = userResponse.data
// convert user API object to user entity
val entities = userList.map { it.toUserEntity() }
// save user list in database
withContext(Dispatchers.IO) { userDao.addUsers(entities) }
// convert user entity to user model
val userItemList = entities.map { it.toUserListItem() }
return Result.Success(userItemList)
} ?: handleFailure(response)
} else {
handleFailure(response)
}
} catch (e: Exception) {
return Result.Failure(e, e.localizedMessage)
}
} else {
// get user list from database if no network
val data = withContext(Dispatchers.IO) { userDao.findAllUsers() }
return if (data.isNotEmpty()) {
Log.d("REPO", "get users from db")
val userItemList = data.map { it.toUserListItem() }
Result.Success(userItemList)
} else {
Result.Failure(Exception("error"), "no network connection")
}
}
}
override suspend fun getUserFullProfile(userId: String): Result<UserFullProfile> {
if (networkStateManager.hasNetWorkConnection()) {
return try {
// get user from user API
val response = userApi.getUserFullProfile(userId)
if (response.isSuccessful) {
Log.d("REPO", "get users from api")
response.body()?.let { userResponse ->
Log.d("REPO", "response:$userResponse")
// convert user API object to user entity
val userEntity = userResponse.toUserEntity()
// save user data in database
withContext(Dispatchers.IO) { userDao.addUserFullProfile(userEntity) }
// convert user entity to user model
val user = userEntity.toUserFullProfile()
return Result.Success(user)
} ?: handleFailure(response)
} else {
handleFailure(response)
}
} catch (e: Exception) {
return Result.Failure(e, e.localizedMessage)
}
} else {
// get user from database if no network
val data = withContext(Dispatchers.IO) { userDao.getUserById(userId) }
return if (data != null) {
Log.d("REPO", "get users from db")
val user = data.toUserFullProfile()
Result.Success(user)
} else {
Result.Failure(Exception("error"), "no network connection")
}
}
}
UserViewModel:
getUserList and getUserFullProfile are use cases which call the repository
class UserViewModel (private val getUserList: GetUserList, private val getUserFullProfile: GetUserFullProfile) : ViewModel() {
val userList = MutableLiveData<List<UserListItem>>()
val userFullProfile = MutableLiveData<UserFullProfile>()
fun getUserList() {
viewModelScope.launch {
when (val result = getUserList.getUserList()) {
is Result.Success -> userList.value = result.successData
is Result.Failure -> result.exception.localizedMessage
}
}
}
fun getUserFullProfile(userId: String) {
viewModelScope.launch {
when (val result = getUserFullProfile.getUserFullProfile(userId)) {
is Result.Success -> userFullProfile.value = result.successData
is Result.Failure -> result.exception.localizedMessage
}
}
}
UserRecyclerAdaper:
class UserRecyclerAdapter(private val context: Context?, val clickListener: UserClickListener) : RecyclerView.Adapter<UserRecyclerAdapter.UserViewHolder>() {
var userList : List<UserListItem> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val inflatedView: UserItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.user_item, parent, false)
return UserViewHolder(inflatedView)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bindUser(position)
}
override fun getItemCount() = userList.size
fun setUsers(users: List<UserListItem>) {
this.userList = users
notifyDataSetChanged()
}
inner class UserViewHolder(private val v: UserItemBinding) : RecyclerView.ViewHolder(v.root) {
fun bindUser(position: Int) {
val item = userList[position]
Log.d("ADAPTER", item.toString())
v.user = item
Picasso.get()
.load(item.picture)
.placeholder(R.drawable.ic_launcher_foreground)
.error(R.drawable.ic_launcher_background)
.into(v.picture)
v.userClickInterface = clickListener
v.root.setOnClickListener {
clickListener.onItemClick(item)
}
}
}
UserListFragment:
class UserListFragment : Fragment(), UserClickListener {
private val userViewModel by viewModel<UserViewModel>()
private lateinit var userAdapter: UserRecyclerAdapter
private lateinit var viewDataBinding: FragmentUserListBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewDataBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_user_list, container, false)
viewDataBinding.lifecycleOwner = this
return viewDataBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
userAdapter = UserRecyclerAdapter(context, this)
recyclerView.adapter = userAdapter
recyclerView.isNestedScrollingEnabled = false
viewDataBinding.viewModel = userViewModel
userViewModel.getUserList()
userViewModel.userList.observe(viewLifecycleOwner, { userList ->
if (userList.isNotEmpty() && userList != null) {
userAdapter.setUsers(userList)
}
})
}
override fun onItemClick(user: UserListItem) {
Log.d("FRAGMENT", user.toString())
userViewModel.getUserFullProfile(user.id)
userViewModel.userFullProfile.observe(viewLifecycleOwner, { userFullProfile ->
Log.d("UFP", userFullProfile.toString())
if (userFullProfile != null) {
(activity as MainActivity).replaceFragment(UserFullProfileFragment.newInstance(userFullProfile),
R.id.fragment_layout, "userFullProfile")
}
})
}
UserFullProfileFragment:
class UserFullProfileFragment : Fragment() {
companion object {
#JvmStatic
fun newInstance(user: UserFullProfile) = UserFullProfileFragment().apply {
arguments = Bundle().apply {
putParcelable("user", user)
}
}
}
private var user: UserFullProfile? = null
private lateinit var mViewDataBinding: FragmentUserFullProfileBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mViewDataBinding.user = user
notify()
Picasso.get()
.load(mViewDataBinding.user?.picture)
.placeholder(R.drawable.ic_launcher_foreground)
.error(R.drawable.ic_launcher_background)
.into(mViewDataBinding.picture)
val dateOfBirth = parseDate(mViewDataBinding.user?.dateOfBirth)
mViewDataBinding.dateOfBirth.text = dateOfBirth
val registerDate = parseDate(mViewDataBinding.user?.registerDate)
mViewDataBinding.registerDate.text = registerDate
}
override fun onAttach(context: Context) {
super.onAttach(context)
user = arguments?.getParcelable("user")
Log.d("APP", user.toString())
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
mViewDataBinding = DataBindingUtil.inflate(inflater,
R.layout.fragment_user_full_profile, container, false)
mViewDataBinding.lifecycleOwner = this
return mViewDataBinding.root
}
Thank you :)
Finally, I found a solution :
I pass the user id argment from UserListFragment to UserFullProfileFragment instead of the current user object and I call the external API to get the current user in the UserFullProfileFragment.
This is the final code:
UserListFragment:
override fun onItemClick(user: UserListItem) {
val action = UserListFragmentDirections.actionUserListFragmentToUserFullProfileFragment(user.id)
findNavController().navigate(action)
}
UserFullProfileFragment:
class UserFullProfileFragment : Fragment() {
private lateinit var userID: String
private var mViewDataBinding: FragmentUserFullProfileBinding? = null
private val binding get() = mViewDataBinding!!
private val userViewModel by viewModel<UserViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.viewModel = userViewModel
userID = UserFullProfileFragmentArgs.fromBundle(requireArguments()).userArgs
userViewModel.getUserFullProfile(userID)
userViewModel.userFullProfile.observe(viewLifecycleOwner, { userFullProfile ->
if (userFullProfile != null) {
binding.user = userFullProfile
Picasso.get()
.load(binding.user?.picture)
.placeholder(R.drawable.ic_launcher_foreground)
.error(R.drawable.ic_launcher_background)
.into(binding.picture)
val dateOfBirth = parseDate(binding.user?.dateOfBirth)
binding.dateOfBirth.text = dateOfBirth
val registerDate = parseDate(binding.user?.registerDate)
binding.registerDate.text = registerDate
}
})
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
mViewDataBinding = DataBindingUtil.inflate(inflater,
R.layout.fragment_user_full_profile, container, false)
binding.lifecycleOwner = this
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
mViewDataBinding = null
}
}

Paging 3 Library calls the load method recursively with LoadType.APPEND

I am trying to display data from IconFinder API. It seems to be ItemKeyedDataSource for me and I used Paging3 to display the data as it's mentioned in the official docs.
Here is code, please check if there're any issues with the implementation I have done and where is the mistake.
IconSetsRemoteMediator
#OptIn(ExperimentalPagingApi::class)
class IconSetsRemoteMediator(
private val query: String?,
private val database: IconsFinderDatabase,
private val networkService: IconFinderAPIService
) : RemoteMediator<Int, IconSetsEntry>() {
private val TAG: String? = IconSetsRemoteMediator::class.simpleName
private val iconSetsDao = database.iconSetsDao
private val remoteKeysDao = database.remoteKeysDao
override suspend fun initialize(): InitializeAction {
// Load fresh data when ever the app is open new
return InitializeAction.LAUNCH_INITIAL_REFRESH
}
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, IconSetsEntry>
): MediatorResult {
val iconSetID = when (loadType) {
LoadType.REFRESH -> {
null
}
LoadType.PREPEND -> {
return MediatorResult.Success(
endOfPaginationReached = true
)
}
LoadType.APPEND -> {
Log.d(TAG, "LoadType.APPEND")
val lastItem = state.lastItemOrNull()
if (lastItem == null) {
return MediatorResult.Success(
endOfPaginationReached = true
)
}
// Get the last item from the icon-sets list and return its ID
lastItem.iconset_id
}
}
try {
// Suspending network load via Retrofit.
val response = networkService.getAllPublicIconSets(after = iconSetID)
val iconSets = response.iconsets
val endOfPaginationReached = iconSets == null || iconSets.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
// Delete the data in the database
iconSetsDao.deleteAllIconSets()
//remoteKeysDao.deleteRemoteKeys()
}
Log.d(TAG, "iconSets = ${iconSets?.size}")
Log.d(TAG, "endOfPaginationReached = $endOfPaginationReached")
Log.d(TAG, "state.anchorPosition = ${state.anchorPosition}")
Log.d(TAG, "state.pages = ${state.pages.size}")
val time = System.currentTimeMillis()
/*val remoteKeys = iconSets!!.map {
RemoteKeysEntry(it.iconset_id, time)
}*/
// Insert new IconSets data into database, which invalidates the current PagingData,
// allowing Paging to present the updates in the DB.
val data = iconSets!!.mapAsIconSetsEntry()
iconSetsDao.insertAllIconSets(data)
// Insert the remote key values which set the time at which the data is
// getting updated in the DB
//remoteKeysDao.insertRemoteKeys(remoteKeys)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
}
IconFinderRepository
class IconFinderRepository(
private val service: IconFinderAPIService,
private val database: IconsFinderDatabase
) {
private val TAG: String? = IconFinderRepository::class.simpleName
fun getPublicIconSets(): Flow<PagingData<IconSetsEntry>> {
Log.d(TAG, "New Icon Sets query")
val pagingSourceFactory = { database.iconSetsDao.getIconSets() }
#OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(pageSize = NUMBER_OF_ITEMS_TO_FETCH, enablePlaceholders = false),
remoteMediator = IconSetsRemoteMediator(
query = null,
database,
service
),
pagingSourceFactory = pagingSourceFactory
).flow
}
companion object {
const val NUMBER_OF_ITEMS_TO_FETCH = 20
}
}
IconSetViewHolder
class IconSetViewHolder private constructor(val binding: RecyclerItemIconSetBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(iconSetsEntry: IconSetsEntry?) {
if (iconSetsEntry == null) {
//Show the Loading UI
} else {
binding.model = iconSetsEntry
binding.executePendingBindings()
}
}
companion object {
fun from(parent: ViewGroup): IconSetViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = RecyclerItemIconSetBinding.inflate(layoutInflater, parent, false)
return IconSetViewHolder(binding)
}
}
}
IconSetAdapter
class IconSetAdapter : PagingDataAdapter<UiModel.IconSetDataItem, ViewHolder>(UI_MODEL_COMPARATOR) {
companion object {
private val UI_MODEL_COMPARATOR =
object : DiffUtil.ItemCallback<UiModel.IconSetDataItem>() {
override fun areContentsTheSame(
oldItem: UiModel.IconSetDataItem,
newItem: UiModel.IconSetDataItem
): Boolean {
return oldItem.iconSetsEntry.iconset_id == newItem.iconSetsEntry.iconset_id
}
override fun areItemsTheSame(
oldItem: UiModel.IconSetDataItem,
newItem: UiModel.IconSetDataItem
): Boolean =
oldItem == newItem
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return if (viewType == R.layout.recycler_item_icon_set) {
IconSetViewHolder.from(parent)
} else {
IconSetViewHolder.from(parent)
}
}
override fun getItemViewType(position: Int): Int {
return when (getItem(position)) {
is UiModel.IconSetDataItem -> R.layout.recycler_item_icon_set
null -> throw UnsupportedOperationException("Unknown view")
else -> throw UnsupportedOperationException("Unknown view")
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val uiModel = getItem(position)
uiModel.let {
when (uiModel) {
is UiModel.IconSetDataItem -> (holder as IconSetViewHolder).bind(uiModel.iconSetsEntry)
}
}
}
}
HomeFragmentViewModel
class HomeFragmentViewModel(application: Application) : AndroidViewModel(application) {
private val TAG: String? = HomeFragmentViewModel::class.simpleName
private val repository: IconFinderRepository = IconFinderRepository(
IconFinderAPIService.create(),
IconsFinderDatabase.getInstance(application)
)
private var iconSetsQueryResult: Flow<PagingData<UiModel.IconSetDataItem>>? = null
fun iconSetsQuery(): Flow<PagingData<UiModel.IconSetDataItem>> {
val newResult: Flow<PagingData<UiModel.IconSetDataItem>> = repository.getPublicIconSets()
.map { pagingData -> pagingData.map { UiModel.IconSetDataItem(it) } }
.cachedIn(viewModelScope)
iconSetsQueryResult = newResult
return newResult
}
/**
* Factory for constructing HomeFragmentViewModel
*/
class Factory(private val application: Application) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(HomeFragmentViewModel::class.java)) {
return HomeFragmentViewModel(application) as T
}
throw IllegalArgumentException("Unable to construct ViewModel")
}
}
}
sealed class UiModel {
data class IconSetDataItem(val iconSetsEntry: IconSetsEntry) : UiModel()
}
IconSetFragment: This is one of the fragments implemented as part of ViewPager. Its parent is a Fragment in an Activity.
class IconSetFragment : Fragment() {
private val TAG: String = IconSetFragment::class.java.simpleName
/**
* Declaring the UI Components
*/
private lateinit var binding: FragmentIconSetBinding
private val viewModel: HomeFragmentViewModel by viewModels()
private val adapter = IconSetAdapter()
private var job: Job? = null
companion object {
fun newInstance(): IconSetFragment {
return IconSetFragment()
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Get a reference to the binding object
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_icon_set, container, false)
Log.d(TAG, "onCreateView")
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initAdapter()
job?.cancel()
job = viewLifecycleOwner.lifecycleScope.launch {
viewModel.iconSetsQuery().collectLatest {
adapter.submitData(it)
Log.d(TAG, "collectLatest $it")
}
}
}
private fun initAdapter() {
binding.rvIconSetList.adapter = adapter
/*.withLoadStateHeaderAndFooter(
header = LoadStateAdapter(), // { adapter.retry() },
footer = LoadStateAdapter { adapter.retry() }
)*/
}
}
IconSetsDao
#Dao
interface IconSetsDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAllIconSets(iconSets: List<IconSetsEntry>)
#Query("SELECT * FROM icon_sets_table")
fun getIconSets(): PagingSource<Int, IconSetsEntry>
#Query("DELETE FROM icon_sets_table")
suspend fun deleteAllIconSets()
}
This is the Logcat screenshot, the load() method is being invoked without any scrolling action.
I have the similar issue, seems the recursive loading issue is fixed by setting the recyclerView.setHasFixedSize(true)

onDetach() not calling in moving from one fragment to another

When i get values back from FragB(A bottomsheetfragment launched from the toolbar of the ativity) to FragA(from viewpager of activity) and call the method of FragA via interface to hit the API's again with the new data it gives me the following exception:
java.lang.IllegalStateException: Can't create ViewModelProvider for detached fragment
Here after getting the data from FragB to activity i am calling method of FragA from activity by using:
val getFragment = pagerAdapter.getItem(viewPager.currentItem)
if(getFragment is GrowthStoryFragment){
getFragment.myfilterOptions(countryId, dateRange, specs)
So after reading from this SO thread it says go for a null check or re-initialize the viewModel in onAttach but the strange behavior here is that no lifecycle method of FragA is being called when i launch the bottomsheetfragment(FragB) from the toolbar of activity and when i dismiss() the FragB on a button click then only the lifecycle methods of FragB are being called and again no lifecycle method of viewpager fragment FragA are calling so when the fragment got detached and now from where i should re-initialize the viewModel and other instances?
Please help me to understand this scenario.
Update:
Here's the Activity code:
class ViewDetailsActivity : BaseActivity(), FilterOptionsDialogFragment.onApplyEventListener, BusinessUnitDialogFragment.afterDoneClick {
private lateinit var pagerAdapter: ViewDetailsFragmentAdapter
private var myFragmentFlag = 0
private var TAG = "ViewDetailsActivity"
override fun getContentView(): Int = R.layout.activity_view_details
override fun onViewReady(savedInstanceState: Bundle?, intent: Intent?) {
tabLayout!!.addTab(tabLayout!!.newTab().setText("Growth Story"))
tabLayout!!.addTab(tabLayout!!.newTab().setText("Share Story"))
tabLayout!!.addTab(tabLayout!!.newTab().setText("Purchase Dynamics"))
tabLayout!!.addTab(tabLayout!!.newTab().setText("Brand Health Track"))
tabLayout.tabMode = TabLayout.MODE_SCROLLABLE
tabLayout.tabGravity = Gravity.CENTER
tabLayout.setTabTextColors(Color.parseColor("#a1a1a1"), Color.parseColor("#ff8a00"))
pagerAdapter = ViewDetailsFragmentAdapter(supportFragmentManager, tabLayout!!.tabCount)
viewPager.adapter = pagerAdapter
viewPager.isFocusableInTouchMode = true
scrollListener()
viewPager!!.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout))
tabLayout!!.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
viewPager!!.currentItem = tab.position
when (tab.position) {
0 -> {
myFragmentFlag = 0
left_scroll.visibility = View.GONE
right_scroll.visibility = View.VISIBLE
}
1 -> {
myFragmentFlag = 1
right_scroll.visibility = View.VISIBLE
}
2 -> {
myFragmentFlag = 2
left_scroll.visibility = View.VISIBLE
right_scroll.visibility = View.VISIBLE
}
3 -> {
myFragmentFlag = 3
left_scroll.visibility = View.VISIBLE
right_scroll.visibility = View.GONE
}
}
}
override fun onTabUnselected(tab: TabLayout.Tab) {
}
override fun onTabReselected(tab: TabLayout.Tab) {
}
})
tvTitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen._16ssp))
tvTitle.setText("Category Deep Dive")
ivBack.setOnClickListener(View.OnClickListener {
finish()
})
ivLogo.setOnClickListener(View.OnClickListener {
val addFrag = FilterOptionsDialogFragment.newInstance()
addFrag.show(supportFragmentManager, "add")
})
vd_edit_icon.setOnClickListener({
val buFrag = BusinessUnitDialogFragment.newInstance()
buFrag.show(supportFragmentManager, "add")
})
vd_edit_icon.visibility = View.VISIBLE
}
fun scrollListener() {
left_scroll.setOnClickListener(View.OnClickListener {
val itemnum = viewPager.currentItem
viewPager.currentItem = itemnum - 1
})
right_scroll.setOnClickListener(View.OnClickListener {
val itemnum = viewPager.currentItem
viewPager.currentItem = itemnum + 1
})
}
override fun someEvent(countryId: String?, dateRange: String?, specs: String?) {
val getFragment = pagerAdapter.getItem(viewPager.currentItem)
if(getFragment is GrowthStoryFragment){
getFragment.myfilterOptions(countryId, dateRange, specs)
}else if(getFragment is ShareStoryFragment){
getFragment.myfilterOptions(countryId, dateRange, specs)
}else if(getFragment is PurchaseDynamicsFragment){
getFragment.myfilterOptions(countryId, dateRange, specs)
}else if(getFragment is BrandHealthFragment){
getFragment.myfilterOptions(countryId, dateRange, specs)
}
}
override fun onDoneClicked(item: String) {
val getFragment = pagerAdapter.getItem(viewPager.currentItem)
if(getFragment is GrowthStoryFragment){
getFragment.getBuName(item)
}else if(getFragment is ShareStoryFragment){
getFragment.getBuName(item)
}else if(getFragment is PurchaseDynamicsFragment){
getFragment.getBuName(item)
}else if(getFragment is BrandHealthFragment){
getFragment.getBuName(item)
}
}
}
Fragment Adapter:
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentStatePagerAdapter
class ViewDetailsFragmentAdapter(supportFragmentManager: FragmentManager,internal var totalTabs: Int): FragmentStatePagerAdapter(supportFragmentManager) {
override fun getItem(position: Int): Fragment? {
when (position) {
0 -> return GrowthStoryFragment()
1 -> return ShareStoryFragment()
2 -> return PurchaseDynamicsFragment()
3 -> return BrandHealthFragment()
else -> return null
}
}
// this counts total number of tabs
override fun getCount(): Int {
return totalTabs
}
}
Fragment A:
class GrowthStoryFragment : Fragment() {
private val TAG = "GrowthStoryFragment"
private lateinit var disposable : Disposable
private lateinit var responseSpinner : List<RespCat>
private lateinit var responseFirstBarChart : List<RespBrand>
private lateinit var RespDon : List<RespDon>
private lateinit var responseSecondBarChart : List<RespDist>
companion object{
private lateinit var myApplicationContext : Context
private var countryID = "1"
private var date = "MAT TY"
private var spec = "val"
private var businessUnitID = "2"
private var category = "Fresh Milk"
private var firstReportTypeId = "1" //fixed for growth story and share story
private var isGroup = "false" //fixed to false
}
private lateinit var userModel : UserViewModel
private val backendApi = WinRetrofitHelper.winApiInstance()
override fun onAttach(context: Context?) {
super.onAttach(context)
Log.e(TAG, "OnAttach")
userModel = ViewModelProviders.of(this)[UserViewModel::class.java]
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
Log.e(TAG, "OnCreateView")
return inflater.inflate(R.layout.fragment_growth_story, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.e(TAG, "OnViewCreated")
myApplicationContext = context!!.applicationContext
getSpinnerResponse(businessUnitID, isGroup,firstReportTypeId)
// getSuperRegionName(countryID, date,spec," ",businessUnitID, category, firstReportTypeId, isGroup)
growth_spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val item = parent?.getItemAtPosition(position) as RespCat
category = item.nameValue
Log.e(TAG,"Category name is: " + category)
getSuperRegionName(countryID, date,spec," ",businessUnitID, category, firstReportTypeId, isGroup)
}
}
}
private fun getSpinnerResponse(businessUnitID: String, isGroup: String, firstReportTypeId: String){
userModel.getResponseGrowthSpinner(businessUnitID, isGroup, firstReportTypeId)
userModel.responseGrowthSpinner.observe(this,
Observer {
Utils.debugger("FRAG ", "$it")
growth_spinner.adapter = GrowthSpinnerAdapter(it)
})
}
private fun getSuperRegionName(countryID: String, date: String, spec: String, superMarket: String,businessUnitID: String, category: String, firstReportTypeId: String, isGroup: String) {
userModel.getResponseSuperRegion(countryID)
userModel.responseSuperRegion.observe(this,
Observer {
Utils.debugger("FRAG ", "$it")
getDataFromApi(countryID, date, spec, it!!.get(0).nameValue, businessUnitID, category, firstReportTypeId, isGroup)
})
}
private fun getColorID(position: Int): Int {
try {
val rnd = Random
when (position) {
0 -> return R.color.brand_almarai
1 -> return R.color.brand_alsafi
2 -> return R.color.brand_nadec
3 -> return R.color.brand_sadafco
4 -> return R.color.brand_nestle
5 -> return R.color.brand_amul
6 -> return R.color.brand_nada
}
return Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256))
}catch (e :Exception){
e.printStackTrace()
}
return 1
}
fun myfilterOptions(countryId: String?, dateRange: String?, specs: String?){
userModel = ViewModelProviders.of(this)[UserViewModel::class.java]
getSuperRegionName(countryId!!,dateRange!!,specs!!.toLowerCase()," ",businessUnitID, category, firstReportTypeId, isGroup)
Log.e(TAG, "Growth Story Fragment:" +countryId!!+" "+dateRange!!+" "+specs!!.toLowerCase()+ " "+businessUnitID+ " "+category+ " "+firstReportTypeId+ " "+isGroup)
}
override fun onDestroy() {
super.onDestroy()
Log.e(TAG, "Ondestroy")
disposable.dispose()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.e(TAG, "OnCreate")
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
Log.e(TAG, "OnActivitycreated")
}
override fun onPause() {
super.onPause()
Log.e(TAG, "OnPause")
}
override fun onStart() {
super.onStart()
Log.e(TAG, "OnStart")
}
override fun onResume() {
super.onResume()
Log.e(TAG, "OnResume")
}
override fun onStop() {
super.onStop()
Log.e(TAG, "OnStop")
}
override fun onDestroyView() {
super.onDestroyView()
Log.e(TAG, "Ondestroyview")
}
override fun onDetach() {
super.onDetach()
Log.e(TAG, "OnDetach")
}
}
FragB code:
class FilterOptionsDialogFragment : BottomSheetDialogFragment(), View.OnClickListener {
private var myResp: List<RespBu>? = null
private lateinit var myView: View
private lateinit var customList: ArrayList<RespBu>
private var dateRange: String = ""
private var specrange: String = ""
private lateinit var onmyApplyEventListener: onApplyEventListener
private var TAG = "FilterOptionsDialogFragment"
val pref: AppPreference by lazy {
AppPreference.getInstance(context!!)
}
companion object {
fun newInstance(): FilterOptionsDialogFragment {
return FilterOptionsDialogFragment()
}
}
override fun onCreateView(inflater: LayoutInflater,
#Nullable container: ViewGroup?,
#Nullable savedInstanceState: Bundle?): View? {
myView = inflater.inflate(R.layout.filter_options_layout, container, false)
Log.e(TAG, "OnCreateView")
// get the views and attach the listener
val backendApi = WinRetrofitHelper.winApiInstance()
val request = backendApi.getBUCountry()
request.enqueue(object : Callback<List<RespBu>> {
override fun onFailure(call: Call<List<RespBu>>?, t: Throwable?) {
}
override fun onResponse(call: Call<List<RespBu>>?, response: Response<List<RespBu>>?) {
val spinner = myView.findViewById<Spinner>(R.id.filter_options_spinner)
spinner.adapter = Spinner_filter_options(getNewList(response?.body()))
if(pref.getString("BuID") != null && !pref.getString("BuID").equals("")){
if(pref.getBoolean(Site.BUSINESS_UNIT_FRONT)!=null && pref.getBoolean(Site.BUSINESS_UNIT_FRONT))
filter_options_spinner.setSelection(pref.getString("BuID").toInt()-1)
else
filter_options_spinner.setSelection(pref.getString("BuID").toInt()-1)
}
}
})
return myView
}
override fun onStart() {
super.onStart()
Log.e(TAG, "OnStart")
}
private fun getNewList(mylist: List<RespBu>?): List<RespBu> {
if(pref.getBoolean(Site.BUSINESS_UNIT_FRONT)!=null && pref.getBoolean(Site.BUSINESS_UNIT_FRONT))
return mylist!!
else{
customList = ArrayList()
customList.add(RespBu("0", 0, "Global", 0))
for (item in mylist.orEmpty()) {
customList.add(item)
}
return customList
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
try {
if (pref.getString("dateName") != null && !pref.getString("dateName").equals("")) {
if (pref.getString("dateName").equals("YTD"))
date_ytd.isChecked = true
else
date_mat.isChecked = true
}
if (pref.getString("specName") != null && !pref.getString("specName").equals("")) {
if (pref.getString("specName").equals("VAL"))
spec_val.isChecked = true
else
spec_vol.isChecked = true
}
val dateradiogroup = view.findViewById<RadioGroup>(R.id.date_radio_group)
val specradiogroup = view.findViewById<RadioGroup>(R.id.spec_radio_group)
view.findViewById<ImageView>(R.id.view_close).setOnClickListener(View.OnClickListener {
dismiss()
})
view.findViewById<Button>(R.id.apply).setOnClickListener(View.OnClickListener {
val dateRadioBtn = view.findViewById<RadioButton>(dateradiogroup.checkedRadioButtonId)
val specRadioBtn = view.findViewById<RadioButton>(specradiogroup.checkedRadioButtonId)
val respBu = view.findViewById<Spinner>(R.id.filter_options_spinner).selectedItem as RespBu
val buName = respBu.keyValue.toString()
val dateName = dateRadioBtn.text.toString()
val specName = specRadioBtn.text.toString()
pref.saveString("BuID", filter_options_spinner.selectedItemId.toString())
pref.saveString("dateName", dateName)
pref.saveString("specName", specName)
pref.saveString("BuName", respBu.keyValue.toString())
onmyApplyEventListener.someEvent(buName, dateName + " TY", specName)
Log.e("Filter item", respBu.nameValue + " " + dateRadioBtn.text)
dismiss()
})
view.findViewById<Spinner>(R.id.filter_options_spinner).onItemSelectedListener = object : OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
}catch (e: Exception){
e.printStackTrace()
}
}
override fun onClick(p0: View?) {
}
interface onApplyEventListener {
fun someEvent(countryId: String?, dateRange: String?, specs: String?)
}
override fun onAttach(activity: Activity?) {
super.onAttach(activity)
Log.e(TAG, "OnAttach")
onmyApplyEventListener = activity as onApplyEventListener
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.e(TAG, "onCreate")
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
Log.e(TAG, "OnActivitycreated")
}
// override fun onAttach(context: Context?) {
// super.onAttach(context)
// onmyApplyEventListener = context as onApplyEventListener
// Log.e(TAG, "OnAttach")
// }
override fun onPause() {
super.onPause()
Log.e(TAG, "OnPause")
}
override fun onResume() {
super.onResume()
Log.e(TAG, "OnResume")
}
override fun onStop() {
super.onStop()
Log.e(TAG, "OnStop")
}
override fun onDestroyView() {
super.onDestroyView()
Log.e(TAG, "Ondestroyview")
}
override fun onDestroy() {
super.onDestroy()
Log.e(TAG, "Ondestroy")
}
override fun onDetach() {
super.onDetach()
Log.e(TAG, "OnDetach")
}
}
Ok. I really think I have it this time:
ViewDetailsFragmentAdapter#getItem is returning a fresh instance every
time. When you later call #getItem, you're getting an un-initialized fragment instance that is also not currently attached to any Activity. As a result, nothing you do will get what you're looking for. By making sure you hand back the exact same instance each time for a given page type, you should be safe.
You have mentioned that FragmentManager#getFragments returns a list that has the fragment you had initialized earlier. You can use this to your advantage by getting the fragment you want by type from the fragments that the given FragmentManager knows about:
class ViewDetailsFragmentAdapter(supportFragmentManager: FragmentManager,internal var totalTabs: Int): FragmentStatePagerAdapter(supportFragmentManager) {
override fun getItem(position: Int): Fragment? {
return when (position) {
0 -> existing<GrowthStoryFragment>() ?: GrowthStoryFragment()
1 -> existing<ShareStoryFragment>() ?: ShareStoryFragment()
2 -> existing<PurchaseDynamicsFragment>() ?: PurchaseDynamicsFragment()
3 -> existing<BrandHealthFragment>() ?: BrandHealthFragment()
else -> return null
}
}
private inline fun <reified T> existing(): T? =
supportFragmentManager.getFragments().firstOrNull { it is T } as T?
SparseArray is just a Map<Int, ?> that's suggested by Android. You can instead track the list of fragments you've handed out inside your adapter instance in one. The upside here is that it's theoretically more performant, and you're keeping knowledge local. The theoretical downside is that you're holding onto framework-managed objects with a potentially different scope than the framework uses.
class ViewDetailsFragmentAdapter(supportFragmentManager: FragmentManager,internal var totalTabs: Int): FragmentStatePagerAdapter(supportFragmentManager) {
private val pages: SparseArray<Fragment> by lazy(:: { SparseArray(totalTabs) }
override fun getItem(position: Int): Fragment? {
return pages.get(position) ?:
when (position) {
0 -> GrowthStoryFragment()
1 -> ShareStoryFragment()
2 -> PurchaseDynamicsFragment()
3 -> BrandHealthFragment()
else -> null
}.also { pages.put(position, it) }
}
ViewModels should be initiated for application context. Like this:
someViewModel = activity.let { ViewModelProvider(requireActivity()).get(SomeViewModel::class.java) }
Although you did not paste proper part of code - I suspect you are tying ViewModel lifecycle to fragment - which you get by requireContext() inside fragment.
Try with requireActivity()
update since code is provided:
userModel = ViewModelProviders.of(this)[UserViewModel::class.java]
this - is reference to Fragment. What I wrote by blind guessing is correct. Use requireActivity()

Retrofit2 always runs on failure in fragment

I am trying to hit api locally but onResponse method is not workingonFailure mehtod always runs
class Categories : Fragment() {
lateinit var categories_recycler: RecyclerView
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val v = inflater!!.inflate(R.layout.fragment_categories, container, false)
categories_recycler = v.findViewById(R.id.categories_recycler)
categories_recycler.setHasFixedSize(true)
categories_recycler.layoutManager = LinearLayoutManager(activity)
retrofit()
return v
}
fun retrofit(){
val retrofit = RetrofitBuilder.instance
val client = retrofit.create(SkillClientRetrofit::class.java)
val call = client.CategoryNames()
call.enqueue(object : Callback< List< GetCatName>> {
override fun onResponse(call: Call< List< GetCatName>>, response: Response< List< GetCatName>>) {
if(response.isSuccessful) {
activity.toast("this is success")
val data = response.body()
categories_recycler.adapter = CategoriesSkillAdapter(data!!)
}
}
override fun onFailure(call: Call< List< GetCatName>>, t: Throwable) {
activity.toast("this is error")
}
})
}
}
Below is RetrofitBuilder:
object RetrofitBuilder {
private var r: Retrofit? = null
val instance:Retrofit
get(){
if(r == null){
r = Retrofit.Builder().baseUrl("<http://192.168.56.1:3000/>")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return r!!
}
}
Below is interface:
public interface SkillClientRetrofit
{ #GET(" category/names") fun CategoryNames(): Call< List< GetCatName>> }
Here is data class:
data class GetCatName(
#SerializedName("_id") val id: String,
#SerializedName("category_name") val categoryName: String )

Categories

Resources