【问题标题】:Android app crashing when trying to set TextView in a Fragment that is dynamically added to a layout尝试在动态添加到布局的 Fragment 中设置 TextView 时,Android 应用程序崩溃
【发布时间】:2017-10-31 13:24:04
【问题描述】:

我正在尝试找到一种方法来动态地将片段添加到活动布局中,同时唯一地设置该片段的 TextViews。假设我在下面有片段类ExampleFragment

package com.project.testapplication;

import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.project.testapplication.R;

public class ExampleFragment extends Fragment {

    TextView Name, Location;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_layout, container, false);
        Name=(TextView)view.findViewById(R.id.name);
        Location=(TextView)view.findViewById(R.id.location);
        return view;
    }

    public void setName(String n)
    {
        Name.setText(n);
    }

    public void setLocation(String loc)
    {
        Location.setText(loc);
    }

}

其中使用fragment_layout.xml的布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="56dp"
        android:text="name"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true" />

    <TextView
        android:id="@+id/location"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/name"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="109dp"
        android:text="location" />
</RelativeLayout>

现在在另一个活动中,我想将用户指定数量的这些片段添加到布局中,并为每个片段添加用户指定的名称和位置(设置为 TextView)。我正在使用 FragmentManager 将片段添加到我的活动中。当我不尝试编辑片段中的任何 TextView(使用片段中的设置器方法)时,片段会正确添加。但是,如果我尝试为 Fragment 中的任何 TextViews 设置文本,应用程序就会崩溃。我在ExampleActivity 类中的代码(包含导致程序崩溃的行):

package com.project.testapplication;

import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.project.testapplication.ExampleFragment;
import com.project.testapplication.ExampleObject;
import com.project.testapplication.R;

import java.util.ArrayList;

public class ExampleActivity extends AppCompatActivity {

    ArrayList<ExampleObject> list = new ArrayList<ExampleObject>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);

        //Populate "list" ArrayList with "ExampleObject" objects
        list.add(new ExampleObject("name1", "location1"));
        list.add(new ExampleObject("name2", "location2"));
        list.add(new ExampleObject("name3", "location3"));

        for(ExampleObject o: list)
        {
            FragmentManager fragmentManager = getFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            ExampleFragment fragment = new ExampleFragment();
            fragment.setName(o.getName()); /***line causes crash***/
            fragment.setLocation(o.getLocation()); /***line causes crash (if not for line above)***/
            fragmentTransaction.add(R.id.linear_layout_scroll, fragment);
            fragmentTransaction.commit();
        }

    }
}

ExampleObject 定义如下:

package com.project.testapplication;

public class ExampleObject {

    String name, location;
    public ExampleObject(String name, String location)
    {
        this.name=name;
        this.location=location;
    }

    public void setName(String name)
    {
        this.name=name;
    }

    public String getName()
    {
        return name;
    }

    public void setLocation(String location)
    {
        this.location=location;
    }

    public String getLocation()
    {
        return location;
    }
}

在我的activity_example.xml 布局中:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/lin_lay"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.project.testapplication.ExampleActivity">

    <ScrollView
        android:id="@+id/scroll_v"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:id="@+id/linear_layout_scroll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
        </LinearLayout>
    </ScrollView>
</LinearLayout>

ExampleActivity 类中,当我删除导致应用程序崩溃的两行代码时,片段添加得很好,但没有自定义的 TextViews。我正在尝试找出一种方法来唯一地设置 TextViews(通过片段中的方法)而不会使应用程序崩溃。

感谢任何回复。谢谢。

编辑:找到了我的问题的解决方案,在下面回答。

【问题讨论】:

    标签: java android android-layout android-fragments dynamic


    【解决方案1】:

    通过查看this 帖子,我终于能够弄清楚了。我必须先将这些方法添加到我的 ExampleFragment 类中:

         public static ExampleFragment newInstance(String name, String location) {
                 ExampleFragment myFragment = new ExampleFragment();
                 Bundle args = new Bundle();
                 args.putString("name", name);
                 args.putString("location", location);
                 myFragment.setArguments(args);
    
                 return myFragment;
        }
    
        @Override
        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            String name = getArguments().getString("name");
            String location = getArguments().getString("location");
            Name.setText(name);
            Location.setText(location);
    

    然后在 ExampleActivity 中,而不是将片段实例化为:

    ExampleFragment fragment = new ExampleFragment();
    

    我将它实例化为:

    ExampleFragment fragment = ExampleFragment.newInstance(o.getName(), o.getLocation());
    

    以及删除 OP 中最初导致其崩溃的两行代码。 (fragment.setName()fragment.setLocation() 语句)。

    问题是我试图在 TextView 初始化之前(在调用 onCreateView 之前)在它们上设置文本。这种实例化片段的方式可确保在创建片段视图之前不会设置 TextView。 This这个帖子也是不错的参考,还有android网站上的documentation

    【讨论】:

      【解决方案2】:

      试试这个代码

      片段

      public class ExampleFragment extends Fragment {
          ArrayList<ExampleObject> list = new ArrayList<ExampleObject>();
          TextView Name, Location;
      
          public ExampleFragment(ArrayList<ExampleObject> arrayList){
              list=arrayList;
          }
      
          @Nullable
          @Override
          public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup 
          container, @Nullable Bundle savedInstanceState) {
              View view = inflater.inflate(R.layout.fragment_layout, container, 
          false);
              Name=(TextView)view.findViewById(R.id.name);
              Location=(TextView)view.findViewById(R.id.location);
              for(ExampleObject o: list)
              {
              setName(o.getName()); 
                  setLocation(o.getLocation());
              }
              return view;
          }
      
          public void setName(String n)
          {
              Name.setText(n);
          }
      
          public void setLocation(String loc)
          {
              Location.setText(loc);
          }
      }
      

      活动

      public class ExampleActivity extends AppCompatActivity {
      
          ArrayList<ExampleObject> list = new ArrayList<ExampleObject>();
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_example);
      
              //Populate "list" ArrayList with "ExampleObject" objects
              list.add(new ExampleObject("name1", "location1"));
              list.add(new ExampleObject("name2", "location2"));
              list.add(new ExampleObject("name3", "location3"));
      
      
                  FragmentManager fragmentManager = getFragmentManager();
                  FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                  ExampleFragment fragment = new ExampleFragment(list);
                  fragmentTransaction.add(R.id.linear_layout_scroll, fragment);
                  fragmentTransaction.commit();
      
          }
      }
      

      【讨论】:

      • 您好,感谢您的回复。我刚刚尝试过,它只使用列表中最后一项的信息显示一个片段。它只显示“name3”和“location3”
      • 你想做什么?
      • 我想要它,以便创建的每个片段都有自己独特的信息。对于这个例子,我希望第一个片段说“name1”和“location1”,然后下面的另一个片段说“name2”和“location2”,下面的另一个片段说“name3”和“location3”。话虽如此,在进一步检查之后,TextViews 的初始化似乎存在问题,因为它每次尝试为 TextView 设置文本时都会抛出 NullPointerException。
      • 为什么要再次分片?你不能替换 TexView 文本吗?
      • 我实际上遇到问题的代码是针对不同的大型应用程序。我为我的问题发布了这段代码,因为它更小更容易理解。两者几乎完全相同,只是规模较小。我需要片段,因为用户将能够生成一定数量的“小部件”作为片段,每个片段都有自己独特的信息。
      猜你喜欢
      • 2014-06-12
      • 1970-01-01
      • 1970-01-01
      • 2021-09-05
      • 1970-01-01
      • 1970-01-01
      • 2014-04-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多