Search code examples
androidkotlinandroid-recyclerviewandroid-gridlayoutgridlayoutmanager

Well working Java code is causing kotlin.TypeCastException: null cannot be cast to non-null type when converted to Kotlin code


I have to add animation to items of RecyclerView and created a CustomGridRecyclerView as given blow:

public class CustomGridRecyclerView extends RecyclerView {

    public CustomGridRecyclerView(Context context) {
        super(context);
    }

    public CustomGridRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomGridRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setLayoutManager(LayoutManager layout) {
        if (layout instanceof GridLayoutManager) {
            super.setLayoutManager(layout);
        } else {
            throw new ClassCastException("This recyclerview should use grid layout manager as the layout manager");
        }
    }

    @Override
    protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params, int index, int count) {

        if (getAdapter() != null && getLayoutManager() instanceof GridLayoutManager) {

            GridLayoutAnimationController.AnimationParameters animationParams =
                    (GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;

            if (animationParams == null) {
                animationParams = new GridLayoutAnimationController.AnimationParameters();
                params.layoutAnimationParameters = animationParams;
            }

            int columns = ((GridLayoutManager) getLayoutManager()).getSpanCount();

            animationParams.count = count;
            animationParams.index = index;
            animationParams.columnsCount = columns;
            animationParams.rowsCount = count / columns;

            final int invertedIndex = count - 1 - index;
            animationParams.column = columns - 1 - (invertedIndex % columns);
            animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns;

        } else {
            super.attachLayoutAnimationParameters(child, params, index, count);
        }
    }
}

it works great, but I want to implement the same in a other App using Kotlin with following code:

class CustomGridRecyclerView : RecyclerView {
    constructor(context: Context?) : super(context!!)
    constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(
        context!!,
        attrs,
        defStyle
    )

    override fun setLayoutManager(layout: LayoutManager?) {
        if (layout is GridLayoutManager) {
            super.setLayoutManager(layout)
        } else {
            throw ClassCastException("This RecyclerView should use GridLayoutManager as the layout manager")
        }
    }

    override fun attachLayoutAnimationParameters(
        child: View?,
        params: ViewGroup.LayoutParams,
        index: Int,
        count: Int
    ) {
        if (adapter != null && layoutManager is GridLayoutManager) {
            //val
            val animationParams =
                params.layoutAnimationParameters as GridLayoutAnimationController.AnimationParameters // CustomGridRecyclerView.kt:38 is the line where kotlin.TypeCastException occurs I have tried with GridLayoutAnimationController.AnimationParameters? as well but never succeeded 
            val columns = (layoutManager as GridLayoutManager?)!!.spanCount
            animationParams.count = count
            animationParams.index = index
            animationParams.columnsCount = columns
            animationParams.rowsCount = count / columns
            val invertedIndex = count - 1 - index
            animationParams.column = columns - 1 - invertedIndex % columns
            animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns
        } else {
            super.attachLayoutAnimationParameters(child, params, index, count)
        }
    }
}

it gives me Error:

kotlin.TypeCastException: null cannot be cast to non-null type android.view.animation.GridLayoutAnimationController.AnimationParameters
        at pk.inlab.app.expenselogger.view.custom.CustomGridRecyclerView.attachLayoutAnimationParameters(CustomGridRecyclerView.kt:38)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3267)
        at android.view.View.draw(View.java:15507)
        at androidx.recyclerview.widget.RecyclerView.draw(RecyclerView.java:4219)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.draw(View.java:15507)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at androidx.coordinatorlayout.widget.CoordinatorLayout.drawChild(CoordinatorLayout.java:1246)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.draw(View.java:15507)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchDraw(PhoneWindow.java:2784)
        at android.view.View.draw(View.java:15507)
        at android.widget.FrameLayout.draw(FrameLayout.java:658)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2763)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:279)
        at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:285)
        at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:335)
        at android.view.ViewRootImpl.draw(ViewRootImpl.java:2939)
        at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2753)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2367)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1292)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6598)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:800)
        at android.view.Choreographer.doCallbacks(Choreographer.java:603)
        at android.view.Choreographer.doFrame(Choreographer.java:572)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786)
        at android.os.Handler.handleCallback(Handler.java:815)
        at android.os.Handler.dispatchMessage(Handler.java:104)
        at android.os.L

how can I cast android.view.animation.GridLayoutAnimationController.AnimationParameters without error?

Update

As suggested in answers below I changed my code

   val animationParams =
                params.layoutAnimationParameters as? GridLayoutAnimationController.AnimationParameters
            val columns = (layoutManager as GridLayoutManager?)!!.spanCount
            animationParams!!.count = count

and getting this:

kotlin.KotlinNullPointerException
        at pk.inlab.app.expenselogger.view.custom.CustomGridRecyclerView.attachLayoutAnimationParameters(CustomGridRecyclerView.kt:40)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3267)
        at android.view.View.draw(View.java:15507)
        at androidx.recyclerview.widget.RecyclerView.draw(RecyclerView.java:4219)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.draw(View.java:15507)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at androidx.coordinatorlayout.widget.CoordinatorLayout.drawChild(CoordinatorLayout.java:1246)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.draw(View.java:15507)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchDraw(PhoneWindow.java:2784)
        at android.view.View.draw(View.java:15507)
        at android.widget.FrameLayout.draw(FrameLayout.java:658)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2763)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:279)
        at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:285)
        at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:335)
        at android.view.ViewRootImpl.draw(ViewRootImpl.java:2939)
        at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2753)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2367)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1292)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6598)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:800)
        at android.view.Choreographer.doCallbacks(Choreographer.java:603)
        at android.view.Choreographer.doFrame(Choreographer.java:572)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786)
        at android.os.Handler.handleCallback(Handler.java:815)
        at android.os.Handler.dispatchMessage(Handler.java:104)
        at android.os.Looper.loop(Looper.java:194)
        at android.app.ActivityThread.main(ActivityThread.java:5631)
        at java.lang

Solution

  • First problem is that you are trying to cast a nullable value (params.layoutAnimationParameters) to a non null type, this causes the null pointer exception when params.layoutAnimationParameters is null.

    To solve this problem use kotlin's safe cast operator (as?) which tries to cast a value to given type and if it cant then it simply returns null.

    second problem is that in your java code if params.layoutAnimationParameters is null then you assign it a new GridLayoutAnimationController.AnimationParameters() ,

    if (animationParams == null) {
       animationParams = new GridLayoutAnimationController.AnimationParameters();
       params.layoutAnimationParameters = animationParams;
    }
    

    I don't see you doing the same thing in kotlin code. I have modified your function to use safe cast and proper null checking and it should work.

    override fun attachLayoutAnimationParameters(
            child: View?,
            params: ViewGroup.LayoutParams,
            index: Int,
            count: Int
    ) {
        if (adapter != null && layoutManager is GridLayoutManager) {
            var animationParams =
                    params.layoutAnimationParameters as? GridLayoutAnimationController.AnimationParameters 
            if (animationParams == null) {
                animationParams = AnimationParameters()
                params.layoutAnimationParameters = animationParams
            }
            val columns = layoutManager.spanCount
            animationParams.count = count
            animationParams.index = index
            animationParams.columnsCount = columns
            animationParams.rowsCount = count / columns
            val invertedIndex = count - 1 - index
            animationParams.column = columns - 1 - invertedIndex % columns
            animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns
        } else {
            super.attachLayoutAnimationParameters(child, params, index, count)
        }
    }