Giving Context to Fragment - android

I'm trying to give context using "this" to a adapter
Code is:
val shoeid = arrayOf(
"Nike air","Adidas","asics"
)
val progress = intArrayOf(
10,50,60
)
shoeArrayList = ArrayList()
for ( i in shoeid.indices ) {
val shoe = UserShoes(shoeid[i], progress[i])
shoeArrayList.add(shoe)
}
binding.listviewProfile.adapter = ShoeAdapter(this,shoeArrayList)
Data Class:
data class UserShoes(var shoeName: String, var usage: Int)
Adapter Code:
class ShoeAdapter(private val context: Activity,private val arrayList: ArrayList<UserShoes>): ArrayAdapter<UserShoes>(context, R.layout.list_view,arrayList) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val inflater: LayoutInflater = LayoutInflater.from(context)
val view: View = inflater.inflate(R.layout.list_view, null)
val shoe: TextView = view.findViewById(R.id.shoeID)
val progress: ProgressBar = view.findViewById(R.id.progressID)
shoe.text = arrayList[position].shoeName
progress.progress = arrayList[position].usage
return super.getView(position, convertView, parent)
}
}
Type mismatch.
Required: Activity

Change
class ShoeAdapter(private val context: Activity,...
to
class ShoeAdapter(private val context: Context,...
and
binding.listviewProfile.adapter = ShoeAdapter(this,shoeArrayList)
to
context?.run { binding.listviewProfile.adapter = ShoeAdapter(this,shoeArrayList) }
You could also keep the Activity but the adapter doesn't need an Activity so it's best to go with the more abstract Context.

Related

Items in RecyclerView are different with ViewBinding

When i make Adapter in regular way, with findViewById it looks ok: like this, but when i do it with view binding looks like this 2
Adapter with ViewBinding:
class HomePageFoldersAdapter() : RecyclerView.Adapter<HomePageFoldersAdapter.FolderHolder>() {
var list = emptyList<Folder>()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = FolderHomeCardBinding.inflate(inflater)
return FolderHolder(binding)
}
override fun onBindViewHolder(holder: FolderHolder, position: Int) {
val folder = list[position]
with(holder.binding) {
val context = root.context
nameTextView.text = folder.name
sizeTextView.text = context.getString(R.string.modules_count, folder.modulesIds.size)
}
}
override fun getItemCount() = list.size
class FolderHolder(val binding: FolderHomeCardBinding) : RecyclerView.ViewHolder(binding.root)
}
try replace instead
val binding = FolderHomeCardBinding.inflate(inflater)
with
val binding = FolderHomeCardBinding.inflate(inflater, parent, false)

I can't to pass context to adapter class

I write an app that will be discovering bluetooth devices and display them in ListView that located in fragment.
This is my code in fragment:
class ScanFragment: Fragment() {
val device_list = ArrayList<DataSource>()
val paired_device_list = ArrayList<DataSource>()
val btAdapter = BluetoothAdapter.getDefaultAdapter()
var rvAdapter: RVAdapter? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view: View? = inflater.inflate(R.layout.scan_fragment, container, false)
val lvDevices = view?.findViewById<ListView>(R.id.lvDevices)
val device1 = DataSource("Test_Xiaomi", "Test_MAC_Address")
val device2 = DataSource("Test_Huawei", "Test_MAC_Adress")
device_list.add(device1)
device_list.add(device2)
lvDevices?.adapter = RVAdapter(activity, R.layout.rv_items, device_list)
rvAdapter?.notifyDataSetChanged()
Log.d("TAG", "Device = $device_list")
val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
activity?.registerReceiver(receiver, filter)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val paired_devices: Set<BluetoothDevice> = btAdapter.bondedDevices
for(device: BluetoothDevice in paired_devices) {
val ok_paired_devices = DataSource(device.name.toString(), device.address.toString())
paired_device_list.add(ok_paired_devices)
}
}
val receiver = object: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val action: String? = intent?.action
if(BluetoothDevice.ACTION_FOUND == action) {
val device: BluetoothDevice? = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
val newDevice = DataSource(device?.name.toString(), device?.address.toString())
device_list.add(newDevice)
val lvDevice = view?.findViewById<ListView>(R.id.lvDevices)
lvDevice?.adapter = RVAdapter(activity, R.layout.rv_items, device_list)
//Log.d("TAG", "Device = $device_list")
}
}
}
override fun onDestroyView() {
super.onDestroyView()
activity?.unregisterReceiver(receiver)
}
And this is of my adapter:
class RVAdapter(context: Context, var res: Int, var list: ArrayList<DataSource>): ArrayAdapter<DataSource>(context, res, list) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
//super.getView(position, convertView, parent)
val inflater = LayoutInflater.from(context)
val row = inflater.inflate(res, parent, false)
val tvDeviceName = convertView?.findViewById<TextView>(R.id.tvDeviceName)
val tvDeviceMAC = convertView?.findViewById<TextView>(R.id.tvDeviceMAC)
var item = list[position]
tvDeviceName?.text = item.device_name
tvDeviceMAC?.text = item.device_mac
return row
}
I just can't to pass context from fragment to my adapter. I tried to pass either "activity" or "context" or " MainActivity()", but ListView doesn't display nothing at all. If i passing exactly "MainActivity()" to adapter, then i getting error message "System services not available to Activities before onCreate()" in line
lvDevices?.adapter = RVAdapter(activity, R.layout.rv_items, device_list).
and in line
class RVAdapter(context: Context, var res: Int, var list: ArrayList<DataSource>): ArrayAdapter<DataSource>(context, res, list)
If i try to pass "activity" then it just highlighted in red color.
When i use recyclerView instead, then everything is fine. But i do not want to use RecyclerView cause i many times tried implementing onItemClick in different ways and nothing working for me because i can't to pass context in onReceive method.
The problem is that activity property returns nullable type, but adapter requires non-nullable type. If you need context in fragment, call requireContext()
RVAdapter(requireContext(), R.layout.rv_items, device_list)

use executePendingBindings() with gridView

I am trying to use executePendingBinding with gridView adapter. On some data change the binding is not executing at first but other UI operations are being executed before that. How do I prevent that. Here is my ImageAdapter :
class ImageAdapter constructor(
private val mContext: Context,
private val resource_layout: Int,
private val viewModelList: ArrayList<ProfileViewModel>
) :
ArrayAdapter<ImageAdapter.ViewHolder>(mContext, resource_layout) {
private lateinit var imageCardBinding: ImageCardBinding
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var convertView = convertView
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(resource_layout, null)
imageCardBinding = DataBindingUtil.bind(convertView)!!
convertView.tag = position
convertView.tag = imageCardBinding
} else imageCardBinding = convertView.tag as ImageCardBinding
imageCardBinding.profileViewModel = viewModelList[position]
imageCardBinding.executePendingBindings()
return imageCardBinding.root
}
override fun getCount(): Int {
return viewModelList.size
}
inner class ViewHolder
}
And on some value successfully changed I am executing this:
gridView.get(imageGridPosition).grid_item_progress.visibility = View.GONE
profile_root.snackBar("Image is set ! You can tap on the image to change it")
But as the binding is not done immediately the gridView.size is 0 and if I comment out the line the picture is set some times later but snackbar is showing before that.
Can it be fixed with executePendingBinding(), if that then how to do that ? or is there any other way?

Is there any way to refresh custom view inside baseadapter after updating data?

I have a custom view inside a baseadapter that is shown on listview. Currently I can update the items inside database but I cannot refresh the view. I want to refresh the view automatically after updating.Is there anyway to refresh? Please take a look at my code.
This is modle class-
class Item_Detail {
var qty:Int = 0
var id:Int = 0
constructor()}
This is DatabaseHandler-
val Detail: List<Item_Detail>
get() {
val bb=this.writableDatabase
val seItem=ArrayList<Item_Detail>()
val myPath=DB_PATH + REAL_DATABASE
val db=SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY)
val selectQuery="Select * From Transaction_table Where date='$rowview_date' And location='$rowview_location'"
val cursor=db.rawQuery(selectQuery, null)
if (cursor.moveToFirst()) {
do {
val item=Item_Detail()
item.id=cursor.getInt(cursor.getColumnIndex("_id2"))
item.qty=cursor.getInt(cursor.getColumnIndex("quantity2"))
seItem.add(item)
} while(cursor.moveToNext())
}
db.close()
return seItem
}
This is MainActivity-
class Detail : AppCompatActivity() {
internal lateinit var db: DataBaseHelper
internal var seItem: List<Item_Detail> = ArrayList<Item_Detail>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
db = DataBaseHelper(this)
detail_date.text = rowview_date.toString()
detail_location.text = rowview_location.toString()
getDetail()
}
private fun getDetail() {
seItem = db.Detail
val adapter = Adapter_detail(this, seItem,this)
list_detail.adapter=adapter
list_detail.invalidate()
}}
This is Adapterclass-
class Adapter_detail(
internal var activity: Activity,
internal var stitem: List<Item_Detail>,
val context:Context
) : BaseAdapter() {
internal lateinit var db: DataBaseHelper
internal var inflater: LayoutInflater
init {
inflater = activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val rowView:View
rowView = inflater.inflate(R.layout.detail_row, null)
rowView.detail_id.text = stitem[position].id.toString()
rowView.detail_qty.text = stitem[position].qty.toString()
rowView.btn_detail.setOnClickListener{
db=DataBaseHelper(context)//Initiate the database
val builder=android.app.AlertDialog.Builder(context)
inflater=activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val view=inflater.inflate(R.layout.edit_layout, null)
builder.setView(view)
val dialog: android.app.AlertDialog=builder.create()
var tran_edit=view.findViewById<EditText>(R.id.edt_edit)
tran_edit.setText(stitem[position].qty.toString())
dialog.show()
var btn_edt=view.findViewById<Button>(R.id.btn_edit)
btn_edt.setOnClickListener {
val item=Item(
Integer.parseInt(stitem[position].id.toString()),
Integer.parseInt(1.toString()),
tran_edit.text.toString(),
name.toString(),
record_date.toString(),
record_location.toString(),
master_record.toString().toInt()
)
db.updateItem(item)
dialog.dismiss()
}
}
return rowView
}
override fun getItem(position: Int): Any {
return stitem[position]
}
override fun getItemId(position: Int): Long {
return stitem[position].id!!.toLong()
}
override fun getCount(): Int {
return stitem.size
}
You need to call notifyDataSetChanged() on your adapter object when you refresh data. You need to do following changes to make use of it,
MainActivity
class Detail : AppCompatActivity() {
internal lateinit var db: DataBaseHelper
internal var seItem: MutableList<Item_Detail> = ArrayList<Item_Detail>()
internal lateinit var adapter : Adapter_detail
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
db = DataBaseHelper(this)
detail_date.text = rowview_date.toString()
detail_location.text = rowview_location.toString()
adapter = Adapter_detail(this, seItem,this)
list_detail.adapter=adapter
getDetail()
}
private fun getDetail() {
adapter.refresh(db.Detail)
}
}
Adapterclass
class Adapter_detail(
internal var activity: Activity,
internal var stitem: MutableList<Item_Detail>,
val context:Context
) : BaseAdapter() {
...
fun refresh(newList: List<Item_Detail>) {
stitem.clear()
stitem.addAll(newList)
notifyDataSetChanged()
}
}
N.B. I wrote this solution using mobile so please excuse any syntax error.
Let me know if it works?
P.S. There is a better adapter implementation i.e. CursorAdapter which is especially designed for using with database cursor. You can follow this tutorial to integrate it.
Update
I forgot one thing, you need to call refresh() inside your ClickListener callback in getView() function where you perform the update operation. So, it will look like as follows,
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
...
btn_edt.setOnClickListener {
val item= ...
db.updateItem(item)
dialog.dismiss()
refresh(db.Detail)
}
}
return rowView
}

How to create object of generic type in a class

I have the following class
open abstract class NexusAdapter<TData: NexusIdProvider, TViewHolder: NexusViewHolder<TData>>
(protected val ctx: Context, private val _layoutId: Int, protected val items: List<TData>):
BaseAdapter() {
override fun getView(position: Int, view: View?, parent: ViewGroup?): View {
val itemView = if (view == null)
LayoutInflater.from(ctx).inflate(_layoutId, parent, false)
else view!!
// How do I create the object of type TViewHolder at runtime????
var viewHolder: TViewHolder = TViewHolder::class.java.newInstance()
viewHolder.bind(itemView , getItem(position))
return itemView
}
//...
}
How do I create the object of type TViewHolder in my class.
You cannot do this. You'd have to provide a factory method for it.
Either have it with an abstract function within the class
abstract fun createViewHolder(): TViewHolder
or provide it as a parameter to the constructor
abstract class NexusAdapter<TData: NexusIdProvider, TViewHolder: NexusViewHolder<TData>>(
protected val ctx: Context,
private val _layoutId: Int,
protected val items: List<TData>,
private val createViewHolder: () -> TViewHolder
) : BaseAdapter()

Categories

Resources