Write a test cases for custom view - android

A custom view contains several attributes as property, to make this code testable than added unit test (RobolectricTestRunner).
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
inflateView()
bindView()
initialize()
setContentHeight()
}
}
private fun inflateView() {
View.inflate(context, R.layout.view_custom, this)
}
private fun bindView() {
panelView = findViewById(R.id.panel_view)
}
private fun setContentHeight(height: Int) {
layoutParams.height = height <-- test case fail due to layoutParams null.
requestLayout()
}
TestCase
#Test
fun init() {
val activity = Robolectric.buildActivity(Activity::class.java).create().get()
var context = mock(Context::class.java)
var typedArray = mock(TypedArray::class.java)
var dropDownView = DropdownTextView(activity, mock(AttributeSet::class.java))
}
layoutParams having a null object and mock not work here.

Related

How to write a test if a view is visible or not when it is set with data binding?

I have a custom view that is initially not visible. However this is determined with a binding adapter. My question as simple as it sounds, how do I write a test to check if the view is visible when the binding adapter method is called?
For example, this is my custom view:
class MyView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
fun setVisibile(visible: Boolean) {
this.visibility = if (visible) VISIBLE else GONE
}
}
And this is the binding adapter method :
#BindingAdapter(“visible)
#JvmStatic
fun setVisible(myView: MyView, someObject: SomeObject?) {
// Some checking on someObject
// ....
myView.setVisible(someObject.someCriteria())
}

How to center children in custom constraint layout programmatically

been at this for a while. Basically I want the views inside my custom ConstraintLayout to be centered programmatically.
My layout looks like this:
class CenterLayout #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs,defStyleAttr) {
private val constraintSet = ConstraintSet()
override fun onFinishInflate() {
super.onFinishInflate()
for (i in 0 until childCount) {
val child = getChildAt(i)
setConstraints(child)
}
constraintSet.applyTo(this)
}
private fun setConstraints(view:View){
constraintSet.clone(this)
constraintSet.centerHorizontally(view.id,ConstraintSet.PARENT_ID)
constraintSet.centerVertically(view.id,ConstraintSet.PARENT_ID)
}
}
Basically I loop through the children and apply some consstraints, but it doesn't seem to be working. The views are still anchored top left.
Any ideas?
Try accesing the layout params of the child view:
class Test(context: Context) : ConstraintLayout(context, null, 0) {
private fun setConstraints(child: View) {
val params = child.layoutParams as ConstraintLayout.LayoutParams
params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID
params.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID
child.layoutParams = params
}
override fun addView(child: View?) {
super.addView(child)
child?.let { setConstraints(it) }
}
}
Also note how i override the addView method for get the child view added to the layout.
Ignore my constructor just take importance on the methods.
Note: I have not tested this code.

How to save scroll of Scroll View properly

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()
}
}

How to use Android ViewBinding for custom view which inflates from existing layout

I see there is ViewBinding support for Activity and Fragment. But how about custom view?
My custom view likes this:
class MyView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
init {
inflate(context, R.layout.my_view, this)
}
}
I try to create ViewBinding but it doesn't work.
private val binding: MyViewBinding
init {
inflate(context, R.layout.my_view, this)
binding = MyViewBinding.inflate(LayoutInflater.from(context))
}
Do you have any solution for ViewBinding with custom view?
you try replace
init {
inflate(context, R.layout.my_view, this)
binding = MyViewBinding.inflate(LayoutInflater.from(context))
}
with
init {
binding = MyViewBinding.inflate(LayoutInflater.from(context),this, false)
}

How to inject viewmodel in a customView with koin?

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()
}

Categories

Resources