I'm dynamically creating views with a method which I add to my layout.
I'm trying to implement another button which is supposed to access the layout and remove them by using a loop, but my app seems to crash consistently.
I'm attempting to do it with a loop because I don't want to remove all of the views through the methods removeAllViews()
or removeAllViewsInLayout()
.
public void RemoveViews() {
ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.constraintId);
// I'd like to keep a text view and a button, but remove the rest.
TextView textView1 = (TextView) findViewById(R.id.textView1);
int textView1Id = textView1.getId();
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
int fabId = fab.getId();
int count = layout.getChildCount();
for(int i=0;i<count;i++) {
View childPos = layout.getChildAt(i);
int childId = layout.getChildAt(i).getId();
// Here I try to detect whether the child is the text view or button.
if(textView1Id == childId || fabId == childId) {}
else {
// The line below causes the crashing.
layout.removeView(childPos);
// This one also crashes:
//layout.removeViewAt(i);
}
}
}
Here is the activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:id="@+id/constraintId"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView1"
android:onClick="onClick_textView1"
android:padding="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:gravity="center"
android:text="@string/text1"
android:textColor="#808080"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_refresh_black_24dp" />
</android.support.constraint.ConstraintLayout>
I think this is the logcat of the error (FATAL EXCEPTION: main):
2018-12-31 14:57:33.004 7507-7507/com.example.test4 E/AndroidRuntime: FATAL
EXCEPTION: main
Process: com.example.test4, PID: 7507
java.lang.IllegalStateException: Could not execute method for android:onClick
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:390)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24774)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:172)
at android.app.ActivityThread.main(ActivityThread.java:6590)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24774)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:172)
at android.app.ActivityThread.main(ActivityThread.java:6590)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.View.getId()' on a null object reference
at com.example.test4.MainActivity.RemoveKeyboardList(MainActivity.java:115)
at com.example.test4.MainActivity.onClick_textView1(MainActivity.java:64)
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24774)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:172)
at android.app.ActivityThread.main(ActivityThread.java:6590)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Basically, I'd like to know how to do this without having the app crashing, and whether there is a better way to remove multiple views from a layout.
//
Solution
Thanks to the suggestion by Anmol, to add the dynamically created views in a separate layout, I achieved what I was looking for, though I couldn't make it work with a loop. I have included my solution here because it's a bit different from his, though I accepted his answer. Sorry, it's the same, but I have included all the steps on how to implement it. Please give credit where it's due.
Step 1:
Add another layout to be included in activity_main.xml. I named it dynamic_content.xml
.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:id="@+id/dynamic_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</android.support.constraint.ConstraintLayout>
Step 2: Include the new layout in activity_main.xml
.
// Add the line below anywhere as far as I know.
<include layout="@layout/dynamic_content" />
Step 3: Include all dynamically created views in the new layout only.
ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.dynamic_content);
// Create new text views, buttons, etc.
layout.addView(newTextView);
Step 4: Create the function to remove them.
public void RemoveViews() {
ConstraintLayout dynamic = (ConstraintLayout) findViewById(R.id.dynamic_content);
dynamic.removeAllViews();
}
Step 5: Add the function to a button.
public void onClick_buttonName(View v)
{
RemoveViews();
}
And it's done - enjoy!
As per your use-case you can add a containerLayout in your layout and add all dynamic view to that container and when you want to remove the dynamic view's just do below.
public void AddViewToContainer(View view){
ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.containerfordynamic_view);
layout.addView(view);
}
public void RemoveViews() {
ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.containerfordynamic_view);
layout.removeAllViews();
}
And Second option is do removeAllView() on your layout and add your editText and Fab button again,
But this should not be preferred as currently you have only two View to retain but if the list grows that is too much overhead for the simple operation so better go with the first solution which is easy to manage and manipulate also.
Thanks.