I've a list of elements, I want to make a click event,I'm trying to use the adapterPosition from bind fun inside the ViewHolder, but it's unresolved reference
I know I should be able to use : bindingAdapterPosition or absoluteAdapterPosition but both of them are unrecognized
please guys, can someone see where is the error in my code?
class MyArtistAdapter(
private var dataSet: List<ArtistItem>,
private val openDetails: ((Int) -> Unit)? = null
) :
RecyclerView.Adapter<MyArtistAdapter.ArtistViewHolder>() {
inner class ArtistViewHolder(private var binding: ArtistItemLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(currentArtist: ArtistItem, openDetails: (Int) -> Unit) {
binding.root.setOnClickListener {
var x = bindingAdapterPosition => error
openDetails(bindingAdapterPosition)=> error
}
binding.tvCollectionArtist.text = currentArtist.collectionName
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArtistViewHolder {
return ArtistViewHolder(
ArtistItemLayoutBinding.inflate(
LayoutInflater.from(parent.context),
parent, false
)
)
}
override fun onBindViewHolder(holder: ArtistViewHolder, position: Int) {
openDetails?.let {
holder.bind(dataSet[position], openDetails)
}
}
override fun getItemCount(): Int {
return dataSet.size
}
}
Related
I'm trying to show images from my Firebase Realtime Database storage. I've done this before with a previous version of my app, but the difference is how I implemented it. My adapter and arraylist class are exactly the same, but instead of using an activity I switched to using fragments.
What I essentially did was copy my old work and make the appropriate changes so I wouldn't run into errors, but unfortunately I ran into some. My images from Firebase are not showing up at all and I'm not sure what is the problem.
Adapter Class
class AbstractAdapter(private val mContext: Context, private val abstractList: ArrayList<Abstract>) : RecyclerView.Adapter<AbstractAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.abstract_image_view, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Glide.with(mContext)
.load(abstractList[position].abstract)
.into(holder.imageView)
}
override fun getItemCount(): Int {
return abstractList.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var imageView: ImageView = itemView.findViewById(R.id.abstractImageView)
}
companion object {
private const val Tag = "RecyclerView"
}
}
Data class
class Abstract {
var abstract: String? = null
constructor() {}
constructor(abstract: String?) {
this.abstract = abstract
}
}
Fragment in which images will be shown
class AbstractWallpapers: Fragment(), PurchasesUpdatedListener {
private lateinit var subscribeAbstract: Button
private var billingClient: BillingClient? = null
lateinit var recyclerView: RecyclerView
lateinit var abstractlist: ArrayList<Abstract>
private var recyclerAdapterAbstract: AbstractAdapter? = null
private var myRef3: DatabaseReference? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_abstract_wallpaper, container, false)
recyclerView = requireView().findViewById(R.id.abstract_recyclerView)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView = view.findViewById(R.id.abstract_recyclerView)
val layoutManager = LinearLayoutManager(requireActivity())
recyclerView.layoutManager = layoutManager
recyclerView.setHasFixedSize(true)
myRef3 = FirebaseDatabase.getInstance().reference
abstractlist = ArrayList()
ClearAll()
GetDataFromFirebase()
subscribeAbstract = view.findViewById(R.id.abstract_subscribe_btn)
subscribeAbstract.setOnClickListener {
subscribeAbstract()
}
// Establish connection to billing client
//check subscription status from google play store cache
//to check if item is already Subscribed or subscription is not renewed and cancelled
billingClient = BillingClient.newBuilder(requireActivity()).enablePendingPurchases().setListener(this).build()
billingClient!!.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
val queryPurchase = billingClient!!.queryPurchases(BillingClient.SkuType.SUBS)
val queryPurchases = queryPurchase.purchasesList
if (queryPurchases != null && queryPurchases.size > 0) {
handlePurchases(queryPurchases)
} else {
saveSubscribeValueToPref(false)
}
}
}
override fun onBillingServiceDisconnected() {
Toast.makeText(requireActivity(), "Service Disconnected", Toast.LENGTH_SHORT).show()
}
})
//item subscribed
if (subscribeValueFromPref) {
subscribeAbstract.visibility = View.GONE
} else {
subscribeAbstract.visibility = View.VISIBLE
}
}
// Code related to Firebase
#SuppressLint("NotifyDataSetChanged")
private fun GetDataFromFirebase() {
val query: Query = myRef3!!.child("Abstract")
query.addListenerForSingleValueEvent(object : ValueEventListener {
#SuppressLint("NotifyDataSetChanged")
override fun onDataChange(snapshot: DataSnapshot) {
for (dataSnapshot: DataSnapshot in snapshot.children) {
val abstract = Abstract()
abstract.abstract = dataSnapshot.child("abstract").value.toString()
abstractlist.add(abstract)
}
recyclerAdapterAbstract = abstractlist.let { AbstractAdapter(requireActivity(), it) }
recyclerView.adapter = recyclerAdapterAbstract
recyclerAdapterAbstract!!.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {}
})
if (recyclerAdapterAbstract != null) recyclerAdapterAbstract!!.notifyDataSetChanged()
}
private fun ClearAll() {
abstractlist.clear()
abstractlist = ArrayList()
}
I fixed my problem. It turns out my rules in Firebase Realtime Database rules for read were set to false instead. My code works perfect. It was only a stupid error on my part.
So, I've converted a code in Android studio from Java to Kotlin and now I'm getting these errors.
Type mismatch: inferred type is String but Regex was expected
Task :app:compileDebugKotlin in app Finished
class MainActivity : AppCompatActivity() {
lateinit var tvUsername: EditText
lateinit var tvPassword: EditText
lateinit var btnRegister: Button
lateinit var img1: ImageView
lateinit var img2: ImageView
lateinit var img3: ImageView
lateinit var img4: ImageView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvUsername = findViewById(R.id.et_userName)
this.tvPassword = findViewById(R.id.et_password)
this.btnRegister = findViewById(R.id.btnRegister)
img1 = findViewById(R.id.img1)
img2 = findViewById(R.id.img2)
img3 = findViewById(R.id.img3)
img4 = findViewById(R.id.img4)
tvPassword.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (hasLength(s)) {
img1.setImageResource(R.drawable.checked)
} else {
img1.setImageResource(R.drawable.unchecked)
}
if (hasSymbol(s)) {
img4.setImageResource(R.drawable.checked)
} else {
img4.setImageResource(R.drawable.unchecked)
}
if (hasDigit(s)) {
img3.setImageResource(R.drawable.checked)
} else {
img3.setImageResource(R.drawable.unchecked)
}
if (hasUppercase(s)) {
img2.setImageResource(R.drawable.checked)
} else {
img2.setImageResource(R.drawable.unchecked)
}
if (hasLength(s) && hasUppercase(s) && hasDigit(s) && hasSymbol(s)) {
btnRegister.setVisibility(View.VISIBLE)
} else {
btnRegister.setVisibility(View.GONE)
}
}
override fun afterTextChanged(s: Editable) {}
})
}
fun hasLength(value: CharSequence): Boolean {
return value.toString().length >= 8
}
fun hasDigit(value: CharSequence): Boolean {
return value.toString() matches("(.*\\d.*)")
}
fun hasUppercase(value: CharSequence): Boolean {
val s = value.toString()
return s != s.isLowerCase()
}
fun hasSymbol(value: CharSequence): Boolean {
val s = value.toString()
return !s.matches("[A-Za-z0-9 ]*")
}
}
above is the complete code. I am getting problem in the hasUppercase() and hasSymbol function.
Please help.
the error is :Type mismatch: inferred type is String but Regex was expected Task :app:compileDebugKotlin in app Finished
Docs
String.matches expects a Regex object as a parameter. Use the String.toRegex method to create one:
s.matches("[A-Za-z0-9 ]*".toRegex())
Edit, for example:
fun hasSymbol(value: String): Boolean {
return !value.matches("[A-Za-z0-9 ]*".toRegex())
}
Edit again
You weren't using the toRegex function for the patterns and s != s.isLowerCase() just doesn't really make sense. The following should work.
fun hasLength(value: CharSequence): Boolean {
return value.length >= 8
}
fun hasDigit(value: CharSequence): Boolean {
return value matches "(.*\\d.*)".toRegex()
}
fun hasUppercase(value: CharSequence): Boolean {
return value matches "[A-Z]*".toRegex()
}
fun hasSymbol(value: CharSequence): Boolean {
return !(value matches "[A-Za-z0-9 ]*".toRegex())
}
I have made an app that uses an API to make a request and then shows the results in a RecyclerView.
I was following a tutorial and in it they used a differCallback and a liveData so the lists updates and such. However whenever the user makes a second search, the recyclerView should only show the new results but the previous results don't dissapear and the new results just appear below the previous search.
I have debugged it for quite some time now but I don't seem to understand where the list is made and where sure it be updated (or in this case cleared).
I tried using .clear() for the diff (Asynclistdiffer), creating a new list in the onBindViewHolder and then clearing that list every time the "search" is called but without any luck. As im still a beginner I don't have a clue on where or what is happening, all your helo will be greatly appreciated
this is my adapter:
class RecipePreviewAdapter : RecyclerView.Adapter<RecipePreviewAdapter.RecipePreviewViewHolder>() {
inner class RecipePreviewViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
private val differCallback = object : DiffUtil.ItemCallback<Recipe>() {
override fun areItemsTheSame(oldItem: Recipe, newItem: Recipe): Boolean {
return oldItem.sourceUrl == newItem.sourceUrl
}
override fun areContentsTheSame(oldItem: Recipe, newItem: Recipe): Boolean {
return oldItem == newItem
}
}
val differ = AsyncListDiffer(this, differCallback)
lateinit var recipeList: MutableList<Recipe>
var isRecipeListInitialized = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipePreviewViewHolder {
return RecipePreviewViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.item_recipe_preview,
parent,
false
)
)
}
override fun onBindViewHolder(holder: RecipePreviewViewHolder, position: Int) {
recipeList = differ.currentList.toMutableList()
isRecipeListInitialized = true
// Log.d(TAG, recipeList.toString())
val recipe = recipeList[position]
holder.itemView.apply {
Glide.with(this).load(recipe.image).into(ivRecipeImagePreview)
tvRecipeTitlePreview.text = recipe.title
tvTimeToMakeTVPreview.text = "${recipe.readyInMinutes} minutes"
tvSummaryPreview.text = Jsoup.parse(recipe.summary).text()
setOnClickListener {
onItemClickListener?.let { it(recipe) }
}
}
}
override fun getItemCount(): Int {
return differ.currentList.size
}
And this is where I call it:
class SearchRecipeFragment : Fragment(R.layout.fragment_search_recipe) {
lateinit var viewModel: RecipeViewModel
lateinit var recipeAdapter: RecipePreviewAdapter
val TAG = "SearchRecipeFragment"
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = (activity as RecipesActivity).viewModel
setupRecyclerView()
recipeAdapter.setOnItemClickListener {
val bundle = Bundle().apply {
putSerializable("recipe", it)
}
findNavController().navigate(
R.id.action_searchRecipeFragment_to_recipeFragment,
bundle
)
}
etSearch.setOnKeyListener(View.OnKeyListener { _, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN) {
if (etSearch.text.toString().isNotEmpty()) {
viewModel.searchRecipe(etSearch.text.toString())
}
return#OnKeyListener true
}
false
})
viewModel.searchRecipeLiveData.observe(viewLifecycleOwner, Observer { response ->
when (response) {
is Resource.Success -> {
hideProgressBar()
response.data?.let { recipeResponse ->
recipeAdapter.differ.submitList(recipeResponse.results.toList())
}
}
is Resource.Error -> {
hideProgressBar()
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
Toast.makeText(activity, "An error occurred: $message", Toast.LENGTH_LONG)
.show()
}
}
is Resource.Loading -> {
showProgressBar()
}
}
})
}
Ans this is the part of the ViewModel that I'm not really sure what it does
private fun handleSearchRecipeResponse(response: Response<SearchRecipeResponse>): Resource<SearchRecipeResponse> {
if (response.isSuccessful) {
response.body()?.let { resultResponse ->
searchRecipesPage++
if (searchRecipesResponse == null) {
searchRecipesResponse = resultResponse
} else {
val oldRecipes = searchRecipesResponse?.results
val newRecipes = resultResponse.results
oldRecipes?.addAll(newRecipes)
}
return Resource.Success(searchRecipesResponse ?: resultResponse)
}
}
return Resource.Error(response.message())
}
too late but it is because of "searchRecipesResponse". You have to set it to "null" and reset searchRecipesPage to 1 for each new search in etSearch.setOnKeyListener().
I'm a Beginner in Kotlin and I want a way to display Interstitial Ad every time I click on the button tvName, but on the contrary, the app crashes whenever I click on the button. I searched for the solution for a long time.
Here's MainActivity
import ...
#Suppress("UNREACHABLE_CODE")
class MainActivity : AppCompatActivity() {
lateinit var mAdView : AdView
var adapter:ChaptersAdapter?=null
var listOfChapters= ArrayList<Chapters>()
#SuppressLint("WrongViewCast")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
MobileAds.initialize(this) {}
mAdView = findViewById(R.id.adView)
val adRequest = AdRequest.Builder().build()
mAdView.loadAd(adRequest)
loadChapters()
adapter = ChaptersAdapter(listOfChapters, this)
lvchapters.adapter = adapter
MobileAds.initialize(this,
"ca-app-pub-3940256099942544~3347511713")
mInterstitialAd = InterstitialAd(this)
mInterstitialAd.adUnitId = "ca-app-pub-3940256099942544/1033173712"
mInterstitialAd.loadAd(AdRequest.Builder().build())
}
fun loadChapters(){
listOfChapters.add(Chapters(" Chapter 1 ", applicationContext.assets.open("Chapter0.txt").bufferedReader().use {
it.readText()
}
))
listOfChapters.add(Chapters(" Chapter 2 ", applicationContext.assets.open("Chapter1.txt").bufferedReader().use {
it.readText()
}
))
}
class ChaptersAdapter: BaseAdapter {
var context:Context?=null
var listOfChaptersLocal= ArrayList<Chapters>()
constructor(listOfChapters:ArrayList<Chapters>,context:Context){
listOfChaptersLocal=listOfChapters
this.context=context
}
override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
val chapters= listOfChaptersLocal[p0]
var inflator= context!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val chaptersView=inflator.inflate(R.layout.list_chapters,null)
chaptersView.tvName.text= chapters.name!!
chaptersView.**tvName.setOnClickListener** {
// **i want to show the Ads in this time frame** but like this example it didn't work
if (mInterstitialAd.isLoaded) {
mInterstitialAd.show()
} else {
Log.d("TAG", "The interstitial wasn't loaded yet.")
}
val intent =Intent(context,ChapterDetails::class.java)
intent.putExtra("name",chapters.name!!)
intent.putExtra("des",chapters.des!!)
context!!.startActivity(intent)
}
return chaptersView
}
override fun getItem(p0: Int): Any {
return listOfChaptersLocal[p0]
}
override fun getItemId(p0: Int): Long {
return p0.toLong()
}
override fun getCount(): Int {
return listOfChaptersLocal.size
}
}
}
I have tried to implement search functionality in the recycler view.for that I have written below code in My "Main.kt" class
search functionality is working fine for me,
the issue is when I am searching the item from the list and clicking on that item I am getting the wrong position.
please help me with this issue.
adapter = DynamicListAdapter(dynamicList)
dynamic_list_recyclerview.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL,false) as RecyclerView.LayoutManager
dynamic_list_recyclerview.adapter = adapter
adapter!!.setRecyclerViewItemClickLister(this)
i have taken edit text and added the addTextChangedListener
edt_search_dynamic.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun afterTextChanged(editable: Editable?) {
filter(editable.toString())
}
})
fun filter(text : String){
var filteredList = mutableListOf<DynamicModel>()
for(list in dynamicList){
if(list.vehicleno.toLowerCase().contains(text.toLowerCase())){
filteredList.add(list)
}
}
adapter?.filterList(filteredList)
}
and this is my adapter class
class DynamicListAdapter (var dynamiclist : List<DynamicModel>) : RecyclerView.Adapter<DynamicListAdapter.DynamicViewHolder>() {
var recyclerViewOnItemClickListener : RecyclerViewOnItemClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DynamicViewHolder {
val inflatedView = LayoutInflater.from(parent.context)
.inflate(R.layout.singlerowdynamiclist,parent,false)
return DynamicViewHolder(inflatedView,dynamiclist)
}
override fun getItemCount(): Int {
return dynamiclist.size
}
override fun onBindViewHolder(holder: DynamicViewHolder, position: Int) {
holder.bindItem(dynamiclist[position])
}
inner class DynamicViewHolder(itemView : View, var dynamiclists : List<DynamicModel>) : RecyclerView.ViewHolder(itemView), View.OnClickListener
{
private var txtStatus : TextView? = null
init {
txtStatus = itemView.findViewById(R.id.txtStatus)
itemView.setOnClickListener(this)
}
fun bindItem(dynamiclist : DynamicModel){
txtStatus?.text = dynamiclist.vehiclestatus
}
override fun onClick(view: View?) {
when(view){
itemView -> if(recyclerViewOnItemClickListener!= null){
recyclerViewOnItemClickListener!!.onItemClick(adapterPosition,view)
}
}
}
}
// method of outer class
fun setRecyclerViewItemClickLister(onRecyclerviewItemClickListener : RecyclerViewOnItemClickListener){
this.recyclerViewOnItemClickListener = onRecyclerviewItemClickListener
}
// for filter the list
fun filterList(dymaniclist : List<DynamicModel>){
this.dynamiclist = dymaniclist
notifyDataSetChanged()
}
}
and this is the interface which I have implemented in my Main.kt class
interface RecyclerViewOnItemClickListener {
fun onItemClick(position : Int, view: View)
}
You won't get the desired position (the position from the unfiltered list) after applying your filters, because you lose that information when creating a new filtered list and putting it inside the DynamicListAdapter.
Let's say inside the adapter we have a list of the following items:
val dynamicList = listOf(
"#0 A", // 0th position
"#1 B", // 1st position
"#2 AB" // 2nd position
)
Now let's use your filter method:
filter("A")
This ends with replacing the old dynamicList inside the DynamicListAdapter with a completely new filteredList, which looks like this:
val filteredList = listOf(
"#0 A", // 0th position
"#2 AB", // 1st position
)
Now when clicking the #2 AB you will get position of value 1 (not desired 2) as it is the item's index in the new filteredList.
There are multiple ways of how to achieve what you want, but if you only want data from the clicked item, just return the object instead of its position on the item click:
interface RecyclerViewOnItemClickListener {
fun onItemClick(dynamicModel: DynamicModel, view: View)
}
// inside DynamicListAdapter
inner class DynamicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItem(dynamiclist : DynamicModel) {
with (itemView) {
txtStatus.text = dynamiclist.vehiclestatus
setOnClickListener {
recyclerViewOnItemClickListener?.onItemClick(dynamiclist, this)
}
}
}
}
However if you need to return the position and the above is not an option, I would suggest leaving the original list in the adapter and letting it handle filtering by itself, for example:
// _dynamicList is always the original list
class DynamicListAdapter (private val _dynamicList: List<DynamicModel>) : RecyclerView.Adapter<DynamicListAdapter.DynamicViewHolder>() {
var recyclerViewOnItemClickListener : RecyclerViewOnItemClickListener? = null
var filterText: String = ""
set(value) {
field = value
notifyDataSetChanged()
}
// return original list (_dynamicList) if no filter should be applied or return the filtered list based on the filterText
private val dynamicList: List<DynamicModel>
get() = if (filterText.isBlank()) _dynamicList
else _dynamicList.filter { it.vehicleno.contains(filterText, ignoreCase = true) }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DynamicViewHolder {
val inflatedView = LayoutInflater.from(parent.context).inflate(R.layout.singlerowdynamiclist,parent,false)
return DynamicViewHolder(inflatedView)
}
override fun getItemCount(): Int {
return dynamicList.size
}
override fun onBindViewHolder(holder: DynamicViewHolder, position: Int) {
holder.bindItem(dynamicList[position])
}
inner class DynamicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItem(dynamicModel: DynamicModel) {
with (itemView) {
txtStatus.text = dynamicModel.vehiclestatus
// get the item's original position (before filtering)
val position = _dynamicList.indexOf(dynamicModel)
setOnClickListener {
recyclerViewOnItemClickListener?.onItemClick(position, this)
}
}
}
}
}
// filter method
fun filter(text: String){
adapter?.filterText = text
}
I put my solution it might help somebody.
Given: recyclerView is working fine.
Problem: on item click is not working after implementing searchview.
solution: add mAdapter.setOnItemClickListener again inside onQueryTextChange as below:
#Override
public boolean onQueryTextChange(String newText) {
mAdapter = new MyAdapter(getApplicationContext(), OtherInofYouWantToPass);
recyclerView.setAdapter(mAdapter);
mAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
#Override
public void onItemClick(View view, MyObj obj, int position) {
{ add your work here}
}
});
return false;
}