NullPointerException เข้าถึงมุมมองใน onCreate ()


114

นี่เป็นคำถามที่ยอมรับได้สำหรับปัญหาที่มักโพสต์บน StackOverflow

ฉันกำลังติดตามบทแนะนำ ฉันได้สร้างกิจกรรมใหม่โดยใช้วิซาร์ด ฉันจะได้รับNullPointerExceptionเมื่อพยายามที่จะเรียกวิธีการในViews รับกับในกิจกรรมของฉันfindViewById()onCreate()

กิจกรรมonCreate():

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

    View something = findViewById(R.id.something);
    something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE

    if (savedInstanceState == null) {
        getSupportFragmentManager().beginTransaction()
                .add(R.id.container, new PlaceholderFragment()).commit();
    }
}

XML เค้าโครง ( fragment_main.xml):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="packagename.MainActivity$PlaceholderFragment" >

    <View
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/something" />

</RelativeLayout>

คำตอบ:


70

บทช่วยสอนอาจล้าสมัยแล้วพยายามสร้าง UI ที่อิงตามกิจกรรมแทนที่จะเป็น UI ตามส่วนที่ต้องการโดยโค้ดที่สร้างโดยวิซาร์ด

มุมมองอยู่ในเค้าโครงส่วนย่อย ( fragment_main.xml) และไม่อยู่ในเค้าโครงกิจกรรม ( activity_main.xml) onCreate()ยังเร็วเกินไปในวงจรชีวิตที่จะพบในลำดับชั้นของมุมมองกิจกรรมและnullจะส่งคืน การเรียกใช้วิธีการที่nullทำให้เกิด NPE

วิธีแก้ปัญหาที่ต้องการคือการย้ายโค้ดไปยังแฟรกเมนต์onCreateView()โดยเรียกfindViewById()ใช้เค้าโครงแฟรกเมนต์ที่พองตัวrootView:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
  View rootView = inflater.inflate(R.layout.fragment_main, container,
      false);

  View something = rootView.findViewById(R.id.something); // not activity findViewById()
  something.setOnClickListener(new View.OnClickListener() { ... });

  return rootView;
}

ตามหมายเหตุด้านข้างเค้าโครงส่วนย่อยจะเป็นส่วนหนึ่งของลำดับชั้นมุมมองกิจกรรมและสามารถค้นพบได้ด้วยกิจกรรมfindViewById()แต่หลังจากเรียกใช้ธุรกรรมแฟรกเมนต์แล้วเท่านั้น รอดำเนินการทำธุรกรรมส่วนได้รับการดำเนินการในหลังจากsuper.onStart()onCreate()


ส่วน findViewById ควรอยู่ใน onActivityCreated
Zar E Ahmer

@ Nepster เป็นได้ แต่ไม่ต้อง
laalto

บางครั้งกิจกรรมยังไม่แนบกับแฟรกเมนต์
Zar E Ahmer

1
@Nepster และที่ว่าทำไมเรียกfindViewById()บนrootViewและไม่เกี่ยวกับกิจกรรม
laalto

10

ลองใช้OnStart()วิธีการและใช้

View view = getView().findViewById(R.id.something);

หรือประกาศมุมมองใด ๆ โดยใช้getView().findViewByIdวิธีการในonStart()

ประกาศผู้ฟังคลิกในมุมมองโดย anyView.setOnClickListener(this);


สำหรับฉันสิ่งนี้มีประโยชน์เพราะฉันพยายามเข้าถึงมุมมองพี่น้องภายใน onCreateView ของแฟรกเมนต์ซึ่งแฟรกเมนต์ถูกประกาศเป็น xml มุมมองพี่น้องยังคงเป็นโมฆะใน onCreateView เนื่องจากผู้ปกครองยังไม่เสร็จสิ้นการขยาย แต่ใน onStart พวกเขามี :)
Daniel Wilson

3

พยายามเปลี่ยนมุมมองการเข้าถึงของคุณเป็นเมธอด onViewCreated ของแฟรกเมนต์เนื่องจากบางครั้งเมื่อคุณพยายามเข้าถึงมุมมองในเมธอด onCreate จะไม่แสดงผลในเวลาที่เกิดข้อยกเว้นของตัวชี้ค่าว่าง

 @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
     View something = findViewById(R.id.something);
     something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE

     if (savedInstanceState == null) {
           getSupportFragmentManager().beginTransaction()
            .add(R.id.container, new PlaceholderFragment()).commit();
    }
 }

2

เห็นด้วยนี่เป็นข้อผิดพลาดทั่วไปเนื่องจากผู้คนมักไม่เข้าใจว่า Fragments ทำงานอย่างไรเมื่อพวกเขาเริ่มทำงานกับการพัฒนา Android เพื่อบรรเทาความสับสนฉันได้สร้างโค้ดตัวอย่างง่ายๆที่ฉันโพสต์บนแอปพลิเคชันถูกหยุดในโปรแกรมจำลอง androidแต่ฉันโพสต์ไว้ที่นี่เช่นกัน

ตัวอย่างมีดังต่อไปนี้:

public class ContainerActivity extends FragmentActivity implements ExampleFragment.Callback
{
    @Override
    public void onCreate(Bundle saveInstanceState)
    {
        super.onCreate(saveInstanceState);
        this.setContentView(R.layout.activity_container);
        if (saveInstanceState == null)
        {               
             getSupportFragmentManager().beginTransaction()
                .add(R.id.activity_container_container, new ExampleFragment())
                .addToBackStack(null)
             .commit();
        }
        getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
        {
            public void onBackStackChanged()
            {
                int backCount = getSupportFragmentManager().getBackStackEntryCount();
                if (backCount == 0)
                {
                    finish();
                }
            }
        });
    }

    @Override
    public void exampleFragmentCallback()
    {
        Toast.makeText(this, "Hello!", Toast.LENGTH_LONG).show();
    }
}

activity_container.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"
    android:orientation="vertical" >

    <FrameLayout
        android:id="@+id/activity_container_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

ExampleFragment:

public class ExampleFragment extends Fragment implements View.OnClickListener
{
    public static interface Callback
    {
        void exampleFragmentCallback();
    }

    private Button btnOne;
    private Button btnTwo;
    private Button btnThree;

    private Callback callback;

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
        try
        {
            this.callback = (Callback) activity;
        }
        catch (ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "Activity must implement Callback interface.", e);
            throw e;
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View rootView = inflater.inflate(R.layout.fragment_example, container, false);

        btnOne = (Button) rootView.findViewById(R.id.example_button_one);
        btnTwo = (Button) rootView.findViewById(R.id.example_button_two);
        btnThree = (Button) rootView.findViewById(R.id.example_button_three);

        btnOne.setOnClickListener(this);
        btnTwo.setOnClickListener(this);
        btnThree.setOnClickListener(this);
        return rootView;
    }

    @Override
    public void onClick(View v)
    {
        if (btnOne == v)
        {
            Toast.makeText(getActivity(), "One.", Toast.LENGTH_LONG).show();
        }
        else if (btnTwo == v)
        {
            Toast.makeText(getActivity(), "Two.", Toast.LENGTH_LONG).show();
        }
        else if (btnThree == v)
        {
            callback.exampleFragmentCallback();
        }
    }
}

fragment_example.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" >

        <Button
            android:id="@+id/example_button_one"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="30dp"
            android:text="@string/hello" 
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"/>

        <Button
            android:id="@+id/example_button_two"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/example_button_one"
            android:layout_alignRight="@+id/example_button_one"
            android:layout_below="@+id/example_button_one"
            android:layout_marginTop="30dp"
            android:text="@string/hello" />

        <Button
            android:id="@+id/example_button_three"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/example_button_two"
            android:layout_alignRight="@+id/example_button_two"
            android:layout_below="@+id/example_button_two"
            android:layout_marginTop="30dp"
            android:text="@string/hello" />

</RelativeLayout>

และนั่นควรเป็นตัวอย่างที่ถูกต้องซึ่งจะแสดงให้เห็นว่าคุณสามารถใช้กิจกรรมเพื่อแสดง Fragment และจัดการกับเหตุการณ์ใน Fragment นั้นได้อย่างไร และวิธีการสื่อสารกับกิจกรรมที่มีอยู่


1
ตัวอย่างนี้ใช้ไลบรารี android-support-v4 สำหรับ FragmentActivity และ support fragment manager
EpicPandaForce

1
ดูตัวอย่างที่สมบูรณ์ยิ่งขึ้นได้ที่stackoverflow.com/questions/24840509/…
EpicPandaForce

1
แม้ว่าคุณควรใช้Ottoเพื่อการสื่อสารแทนการโทรกลับและใช้Butterknifeเพื่อฉีดมุมมอง
EpicPandaForce

2

มุมมอง "บางสิ่ง" อยู่ในส่วนย่อยและไม่ได้อยู่ในกิจกรรมดังนั้นแทนที่จะเข้าถึงในกิจกรรมคุณต้องเข้าถึงในคลาสแฟรกเมนต์เช่น

ใน PlaceholderFragment.class

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_main, container,
  false);

View something = root .findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... });

return root;
}

2

คุณกำลังพยายามเข้าถึงองค์ประกอบ UI ในonCreate()แต่ยังเร็วเกินไปที่จะเข้าถึงองค์ประกอบเหล่านี้เนื่องจากในมุมมองส่วนย่อยสามารถสร้างได้ในonCreateView()วิธีการ และonActivityCreated()วิธีการมีความน่าเชื่อถือในการจัดการการกระทำใด ๆ กับพวกเขาเนื่องจากกิจกรรมถูกโหลดอย่างเต็มที่ในสถานะนี้


1

เพิ่มสิ่งต่อไปนี้ในactivity_main.xmlของคุณ

<fragment
    android:id="@+id/myFragment"
    android:name="packagename.MainActivity$PlaceholderFragment"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >
</fragment>

0

เนื่องจากคุณได้ประกาศมุมมองของคุณfragment_main.xmlแล้วให้ย้ายส่วนของโค้ดที่คุณได้รับ NPE ในonCreateView()วิธีการของแฟรกเมนต์ สิ่งนี้ควรแก้ปัญหาได้


0

ในโค้ดที่โพสต์ด้านบนในคำถามมีปัญหา: คุณกำลังใช้ R.layout.activity_main ในวิธี oncreate แต่ชื่อไฟล์ xml คือ "fragment_main.xml" หมายความว่าคุณกำลังพยายามรับมุมมองของไฟล์ fragment_main.xml ซึ่งไม่ได้แสดงดังนั้นจึงให้ข้อยกเว้นของตัวชี้ว่าง เปลี่ยนรหัสเช่น:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.fragment_main);// your xml layout ,where the views are

    View something = findViewById(R.id.something);
    something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE

    if (savedInstanceState == null) {
        getSupportFragmentManager().beginTransaction()
                .add(R.id.container, new PlaceholderFragment()).commit();
    }
}

0

คุณต้องจำไว้ว่าสิ่งสำคัญคือ: NullPointerException เกิดขึ้นเมื่อคุณได้ประกาศตัวแปรของคุณและพยายามเรียกคืนค่าก่อนกำหนดค่าให้กับมัน


0

ใช้onViewCreated () Method ทุกครั้งที่ใช้หรือเรียกดูจากส่วนย่อย

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
      View v = view.findViewById(R.id.whatever)
}

0

ฉันมีการNullPointerExceptionเริ่มต้นฟังก์ชั่นเดียวกันหลังจากโทรfindViewById() onCreate()และonCreateView()วิธีการ

แต่เมื่อฉันได้ใช้onActivityCreated(Bundle savedInstanceState) {...}มันได้ผล ฉันจึงสามารถเข้าถึงGroupViewและตั้งค่าผู้ฟังของฉันได้

ฉันหวังว่ามันจะเป็นประโยชน์


0

ไลบรารียอดนิยมสำหรับการค้นหามุมมองที่นักพัฒนาเกือบทุกคนใช้

มีดตัดเนย

ตามที่ฉันสามารถมีคำตอบเพียงพอที่อธิบายการค้นหามุมมองด้วยวิธีการที่เหมาะสม แต่ถ้าคุณเป็นนักพัฒนาแอนดรอยด์และเขียนโค้ดเป็นประจำทุกวันคุณสามารถใช้มีดตัดเนยซึ่งช่วยประหยัดเวลาในการค้นหามุมมองได้มากและคุณไม่ได้เขียนโค้ดสำหรับมันด้วย 2-3 ขั้นตอนคุณจะพบมุมมองในหน่วยมิลลิวินาที .

เพิ่มการพึ่งพาในการไล่ระดับระดับแอป:

implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

เพิ่มปลั๊กอินสำหรับมีดเนย:

File -> Settings -> plugins-> 

จากนั้นค้นหาAndroid ButterKnife Zeleznyและติดตั้งปลั๊กอินและรีสตาร์ทสตูดิโอของคุณเท่านี้ก็เสร็จแล้ว

ตอนนี้เพียงไปที่วิธี Oncreate ของกิจกรรมของคุณและคลิกขวาที่ layout_name ของคุณแล้วแตะที่ปุ่มสร้างและเลือกตัวเลือกการฉีดบัตเตอร์ไนฟ์และการอ้างอิงมุมมองของคุณจะถูกสร้างขึ้นโดยอัตโนมัติตามที่กล่าวไว้ด้านล่าง:

    @BindView(R.id.rv_featured_artist)
    ViewPager rvFeaturedArtist;
    @BindView(R.id.indicator)
    PageIndicator indicator;
    @BindView(R.id.rv_artist)
    RecyclerView rvArtist;
    @BindView(R.id.nsv)
    NestedScrollingView nsv;
    @BindView(R.id.btn_filter)
    Button btnFilter;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.