I am trying to implement SearchView with animation,
SearchView is used in an activity_location.xml
cl_toolbar in activity_location overlaps the SearchView
hence I tried to change its visibility to INVISIBLE when openSearch() called and again VISIBLE when closeSearch() called.
getting a NullPointerException error in SearchView when tried to implement above things.
FATAL EXCEPTION: main Process: com.virusnetic.meather, PID: 11427 java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.constraintlayout.widget.ConstraintLayout.setVisibility(int)' on a null object reference at com.virusnetic.meather.views.SearchView._init_$lambda$0(SearchView.kt:26)
at com.virusnetic.meather.views.SearchView.$r8$lambda$Mcmg8MRJAUGTfnVTINGMDsQmb5g(Unknown Source:0)
at com.virusnetic.meather.views.SearchView$$ExternalSyntheticLambda0.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7441)
at android.view.View.performClickInternal(View.java:7418)
at android.view.View.access$3700(View.java:835)
at android.view.View$PerformClick.run(View.java:28676)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
SearchView.kt
package com.virusnetic.meather.views
import android.animation.Animator
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewAnimationUtils
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.RelativeLayout
import androidx.constraintlayout.widget.ConstraintLayout
import com.virusnetic.meather.R
class SearchView(
context: Context,
attrs: AttributeSet
) : FrameLayout(context, attrs) {
init {
LayoutInflater.from(context)
.inflate(R.layout.view_search, this, true)
findViewById<View>(R.id.open_search_button).setOnClickListener {
// Set Location toolbar to Invisible
findViewById<ConstraintLayout>(R.id.cl_ToolBar).visibility = View.INVISIBLE
openSearch()
}
findViewById<View>(R.id.close_search_button).setOnClickListener {
closeSearch()
// Set Location toolbar to GONE
findViewById<ConstraintLayout>(R.id.cl_ToolBar).visibility = View.VISIBLE
}
}
private fun openSearch() {
val open_search_button: View = findViewById(R.id.open_search_button)
val search_input_text: EditText = findViewById(R.id.search_input_text)
val search_open_view: RelativeLayout = findViewById(R.id.search_open_view)
search_input_text.setText("")
search_open_view.visibility = View.VISIBLE
val circularReveal = ViewAnimationUtils.createCircularReveal(
search_open_view,
(open_search_button.right open_search_button.left) / 2,
(open_search_button.top open_search_button.bottom) / 2,
0f, width.toFloat()
)
circularReveal.duration = 300
circularReveal.start()
}
private fun closeSearch() {
val open_search_button: View = findViewById(R.id.open_search_button)
val search_input_text: EditText = findViewById(R.id.search_input_text)
val search_open_view: RelativeLayout = findViewById(R.id.search_open_view)
val circularConceal = ViewAnimationUtils.createCircularReveal(
search_open_view,
(open_search_button.right open_search_button.left) / 2,
(open_search_button.top open_search_button.bottom) / 2,
width.toFloat(), 0f
)
circularConceal.duration = 300
circularConceal.start()
circularConceal.addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) = Unit
override fun onAnimationCancel(animation: Animator?) = Unit
override fun onAnimationStart(animation: Animator?) = Unit
override fun onAnimationEnd(animation: Animator?) {
search_open_view.visibility = View.INVISIBLE
search_input_text.setText("")
circularConceal.removeAllListeners()
}
})
}
}
also the layout where I used the view
view_search.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="@ id/frame"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="@color/background">
<RelativeLayout
android:id="@ id/search_closed_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:visibility="visible"
android:background="@color/background">
<View
android:id="@ id/open_search_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="16dp"
android:background="@drawable/search"
android:backgroundTint="@color/translucent"/>
</RelativeLayout>
<RelativeLayout
android:id="@ id/search_open_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="4dp"
android:background="@drawable/rounded_corner_background"
android:visibility="invisible">
<View
android:id="@ id/close_search_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginStart="16dp"
android:background="@drawable/ic_round_close_24"
android:backgroundTint="@color/translucent"/>
<EditText
android:id="@ id/search_input_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_toStartOf="@id/execute_search_button"
android:layout_toEndOf="@id/close_search_button" />
<!-- TODO add add button -->
<View
android:id="@ id/execute_search_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="16dp"
android:background="@drawable/add"
android:backgroundTint="@color/translucent"/>
</RelativeLayout>
</FrameLayout>
activity_location.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background"
android:padding="30dp"
tools:context=".activities.LocationsActivity">
<!-- Toolbar -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@ id/cl_ToolBar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:elevation="1dp"
android:layout_marginTop="20dp"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@ id/ll_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/backicon" />
<TextView
android:id="@ id/tv_CurrentLocation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="Select City"
android:textColor="@color/translucent"
android:textSize="18sp"
android:fontFamily="@font/ubuntu_condensed_regular" />
</LinearLayout>
<ImageButton
android:id="@ id/ib_currentLocations"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="60dp"
android:background="@drawable/current_location"
android:backgroundTint="@color/translucent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- Search View -->
<com.virusnetic.meather.views.SearchView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@ id/rv_locations"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/location_list_item"
tools:itemCount="3"
app:layout_constraintTop_toBottomOf="@id/cl_ToolBar" />
</androidx.constraintlayout.widget.ConstraintLayout>
CodePudding user response:
Here in
findViewById<ConstraintLayout>(R.id.cl_ToolBar).visibility = View.VISIBLE
R.id.cl_ToolBar
, in SearchView.kt, is not part of view_search.xml
That is why is throwing null pointer exception.
CodePudding user response:
You will have to provide a way how your search view can access it. As you used it, it tried to find the cl_Toolbar in the SearchView - View can't find view that is higher in the hierarchy*.
class SearchView(
context: Context,
attrs: AttributeSet
) : FrameLayout(context, attrs) {
private var toolbarVisibilityHandler? = null
init {
LayoutInflater.from(context).inflate(R.layout.view_search, this, true)
findViewById<View>(R.id.open_search_button).setOnClickListener {
// Set Location toolbar to Invisible
// findViewById<ConstraintLayout>(R.id.cl_ToolBar).visibility = View.INVISIBLE
toolbarVisibilityHandler?.hideToolbar()
openSearch()
}
findViewById<View>(R.id.close_search_button).setOnClickListener {
closeSearch()
// Set Location toolbar to GONE
// findViewById<ConstraintLayout>(R.id.cl_ToolBar).visibility = View.VISIBLE
toolbarVisibilityHandler?.showToolbar()
}
}
fun setToolbarVisibilityHandler(handler: ToolbarVisibilityHandler?) {
toolbarVisibilityHandler = handler
}
...
}
I would suggest that you create some interface for callbacks that show/hide the Toolbar.
interface ToolbarVisibilityHandler {
fun showToolbar()
fun hideToolbar()
}
Then in the place where you initiate the views you set the handler in SearchView.
<!-- Search View -->
<com.virusnetic.meather.views.SearchView
android:id="@ id/searchView"
.../>
override fun onCreate() {
...
val toolbarView = findViewById<ConstraintLayout>(R.id.cl_ToolBar)
findViewById<SearchView>(R.id.searchView).setToolbarVisibilityHandler(object: ToolbarVisibilityHandler {
override fun showToolbar() {
toolbarView.visibility = View.VISIBLE
}
override fun hideToolbar() {
toolbarView.visibility = View.INVISIBLE
}
})
}