In case of below, MyView's callback is not mutable property so I want to declare as val.
Not only that, Since it can not be initialized at init function, it is error prone.
How to set callback as val or... how to set callback in the init function?
Thanks
class MyView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
lateinit var callback: Callback
init {
//callback =
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.main_activity)
my_view.callback = object : Callback {
}
}
}
<LinearLayout>
<com.example.MyView
android:id="#+id/my_view/>
</LinearLayout>
If you want to set callback from another class, e.g. Activity, this property must be mutable:
class MyView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
var callback: Callback? = null
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.main_activity)
my_view.callback = object : Callback {
}
}
}
If you want callback to be immutable you should initialize it in MyView class:
class MyView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
val callback: Callback = object : Callback {
}
}
But in this case you won't be able to update it from another class.
Related
enter image description here
I AM trying to create other constructor in recyclerView adapter but getting error
please tell me how to create secondary constructor in recycler adapter taking arraylist parameter in kotlin android studio
class CustomRecyclerAdapter constructor(
val context1: Context,
val topics: Array<String>,
private var alist: ArrayList<Array<String>>
) : RecyclerView.Adapter<CustomRecyclerAdapter.ViewHolder>() {
constructor(sss: ArrayList<Array<String>>, ttt: Array<String>) : this(sss) {
this.alist = sss
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): CustomRecyclerAdapter.ViewHolder {
val layout = LayoutInflater.from(parent.context).inflate(R.layout.cardlayout, parent, false)
return ViewHolder(layout)
}
override fun onBindViewHolder(holder: CustomRecyclerAdapter.ViewHolder, position: Int) {
holder.topicTextView.text = topics[position]
}
override fun getItemCount(): Int {
return topics.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var topicTextView: TextView = itemView.findViewById(R.id.gir_topics_tvid)
}
}
Please use these constructors:
class CustomRecyclerAdapter constructor(
val context: Context,
val topics: Array<String> = emptyArray(),
private var alist: ArrayList<String>
) : RecyclerView.Adapter<CustomRecyclerAdapter.ViewHolder>() {
constructor(context: Context, sss: ArrayList<String>) : this(context, alist = sss)
// ...
}
In the primary constructor you need to specify a default value for topics. Also add context: Context parameter to the secondary constructor and pass it to this() when calling the primary constructor.
How to save scroll state of scrollview properly.In my code, I'm using :
scroll_x = scrollView.getScrollX();
scroll_y = scrollView.getScrollY();
when activity pause,i'm stored x and y as you can see here, and when activity start, i'm scroll scrollView to x and y.
But crux is (main problem) is, scrollview not scrollview to x and y properly, it scroll up or down a little bit automatically. How to fix it?
You can manage the instance state by using this class:
class SaveScrollNestedScrollViewer : NestedScrollView {
constructor(context: Context) : super(context)
constructor(context: Context, attributes: AttributeSet) : super(context, attributes)
constructor(context: Context, attributes: AttributeSet, defStyleAttr: Int) : super(context, attributes, defStyleAttr)
public override fun onSaveInstanceState(): Parcelable? {
return super.onSaveInstanceState()
}
public override fun onRestoreInstanceState(state: Parcelable?) {
super.onRestoreInstanceState(state)
}
}
use on your xml:
<yourClassNamePlace.SaveScrollNestedScrollViewer
android:id="#+id/my_scroll_viewer"
android:layout_width="match_parent"
android:layout_height="match_parent">
</yourClassNamePlace.SaveScrollNestedScrollViewer>
and then use in activity like this:
class MyActivity : AppCompatActivity() {
companion object {
var myScrollViewerInstanceState: Parcelable? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.my_activity)
if (myScrollViewerInstanceState != null) {
my_scroll_viewer.onRestoreInstanceState(myScrollViewerInstanceState)
}
}
public override fun onPause() {
super.onPause()
myScrollViewerInstanceState = my_scroll_viewer.onSaveInstanceState()
}
}
When I programmatically added MyView and call myView.invalidate(), onDraw is not called on demand. But when I put it directly on R.layout.activity_main it works like no problem.
On my activity
val myView: MyView
get() = MyView(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
container.addView(myView)
}
MyView
class MyView : View{
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
Log.e("MyView","onDraw")
}
}
Oh I see, this is the problem
val myView: MyView
get() = MyView(this)
This will create new instance of MyView every time I use the variable myView
So I make it like this, now it works.
lateinit var myView: MyView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myView = MyView(this)
setContentView(R.layout.activity_graph)
container.addView(myView)
}
I know how to inject viewmodel in Activities or Fragments with koin:
private val regionSelectorViewModel: RegionSelectorViewModel by viewModel()
Right now I am setting viewmodel to my customView like this:
fun setViewModel(viewModel: RegionSelectorViewModel) {
mViewModel = viewModel
}
The viewmodel is initialized in Activity and passed through parameter to view. But... I would like to inject viewmodels in customViews as I do in the activities or fragments. Is there a way to do that using koin?
In the end I got a solution for this problem, we only have to get viewmodel from activity context:
private val viewModel: VersionViewModel by lazy {
(context as FragmentActivity).getViewModel()
}
Another solution, or for me the best solution is create a delegate to get viewmodel from a view.
inline fun <reified T : ViewModel> ViewGroup.viewModel(): ReadOnlyProperty<ViewGroup, T> =
object : ReadOnlyProperty<ViewGroup, T> {
private var viewModel: T? = null
override operator fun getValue(
thisRef: ViewGroup,
property: KProperty<*>
): T = viewModel ?: createViewModel(thisRef).also { viewModel = it }
private fun createViewModel(thisRef: ViewGroup): T {
return (thisRef.context as FragmentActivity).getViewModel()
}
}
class CustomView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
private val viewModel: CustomViewModel by viewModel()
}
I'm newbie in Kotiln and this code is simple class which I'm using on Android. In this class I want to make simple listener to detect clicks on item inside of a class such as activity, fragment or adapter. My problem is that I can define this listener for circularProgressView widget.
class DownloadProgressView : RelativeLayout {
var progressListener: ((downloading: Boolean) -> Unit)? = null
private var downloading = false
constructor(context: Context?) : super(context) {
init()
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init()
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init()
}
private fun init() {
LayoutInflater.from(context).inflate(R.layout.download_progress_layout, this)
circularProgressView.setOnClickListener {
downloading = !downloading
setProgressViews(downloading)
progressListener?.invoke(downloading)
//if listener!=null then onItemClicked.onClick();
}
}
}
How can I know if user clicked on circularProgressView inside activity?
I'm trying to implement this interface and use it inside Kotlin:
public interface OnItemClickListener {
void onClick(int position);
}
For init progreessListener use following code
var listener: () -> Unit = {}
For call onClick use : listener()
For implement your interface use following code :
var onItemClickInterface : () -> Unit = {
//TODO
}