Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีใช้ findViewById ใน Fragment

ภาพรวม

ตัวอย่างนี้แสดงวิธีใช้ findViewById ใน Fragment อย่างถูกต้อง โดยสำคัญคือต้องเรียกผ่าน View ที่ inflate ไม่ใช่ผ่าน Activity

ขั้นตอนการสร้าง

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

เปิด Android Studio ไปที่ File → New Project และกรอกข้อมูลที่จำเป็น

ขั้นตอนที่ 2: Layout หลัก (res/layout/activity_main.xml)

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

    <LinearLayout
        android:id="@+id/linearlayout01"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#ccc"
        android:orientation="vertical">
        <fragment
            android:name="com.example.myapplication.FirstFragment"
            android:id="@+id/frag_1"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/linearlayout02"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#eee"
        android:orientation="vertical">
        <fragment
            android:name="com.example.myapplication.SecondFragment"
            android:id="@+id/frag_2"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

</LinearLayout>

ขั้นตอนที่ 3: Activity หลัก (src/MainActivity.java)

package com.example.myapplication;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentActivity;

public class MainActivity extends FragmentActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

ขั้นตอนที่ 4: Fragment แรก (src/FirstFragment.java)

package com.example.myapplication;

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

public class FirstFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        // 1. Inflate Layout → ได้ root View
        View rootView = inflater.inflate(R.layout.fragment, container, false);
        
        // 2. ใช้ rootView.findViewById() ไม่ใช่ getActivity().findViewById()
        TextView textView = rootView.findViewById(R.id.text);
        textView.setText("First Fragment - Click Me");
        
        // 3. Set Listener
        textView.setOnClickListener(v -> {
            // ทำงานเมื่อคลิก
        });
        
        return rootView;
    }
}

ขั้นตอนที่ 5: Fragment ที่สอง (src/SecondFragment.java)

package com.example.myapplication;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class SecondFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        // Inflate และ return ทันที (ไม่ต้อง findViewById ถ้าไม่ได้ใช้)
        return inflater.inflate(R.layout.fragment, container, false);
    }
}

ขั้นตอนที่ 6: Layout Fragment (res/layout/fragment.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:gravity="center"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/text"
        android:textSize="30sp"
        android:text="Click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

หลักการสำคัญ: findViewById ใน Fragment

✅ ถูกต้อง

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment, container, false);
    TextView tv = view.findViewById(R.id.text); // ใช้ View ที่ inflate
    return view;
}

❌ ผิด - ใช้ Activity.findViewById()

// ไม่ดี - หา View ใน Activity Layout ไม่เจอ (Fragment มี Layout แยก)
TextView tv = getActivity().findViewById(R.id.text); // Null หรือเจอ View ผิด

❌ ผิด - ใช้ findViewById() โดยตรง (ใน Fragment)

// Fragment ไม่มี findViewById() method (แค่ Activity และ View มี)
TextView tv = findViewById(R.id.text); // Compile Error

ViewBinding (แนะนำสำหรับโปรเจกต์ใหม่)

// build.gradle (app)
android {
    buildFeatures {
        viewBinding true
    }
}

// Fragment
private FragmentBinding binding;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    binding = FragmentBinding.inflate(inflater, container, false);
    binding.textView.setText("Hello ViewBinding");
    return binding.getRoot();
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    binding = null; // ป้องกัน Memory Leak
}

DataBinding (ทางเลือกอื่น)

// build.gradle
buildFeatures {
    dataBinding true
}

// Layout (layout/fragment.xml)
<layout>
    <data>
        <variable name="viewModel" type="com.example.MyViewModel"/>
    </data>
    <LinearLayout ...>
        <TextView android:text="@{viewModel.title}" />
    </LinearLayout>
</layout>

// Fragment
FragmentBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment, container, false);
binding.setViewModel(viewModel);
binding.setLifecycleOwner(this);
return binding.getRoot();

Lifecycle และ View Access

Lifecycle MethodView AvailablefindViewById
onAttach()ไม่ไม่ได้
onCreate()ไม่ไม่ได้
onCreateView()✅ เริ่มมี✅ ผ่าน inflated View
onViewCreated()✅✅ แนะนำทำที่นี่
onStart/onResume✅✅
onPause/onStop✅✅
onDestroyView()❌ ถูกทำลาย❌ ห้ามใช้
onDetach()ไม่ไม่ได้

Best Practices

  1. ใช้ ViewBinding แทน findViewById (Type-safe, Null-safe, Fast)
  2. ทำการ findView/Setup UI ใน onViewCreated() ไม่ใช่ onCreateView()
  3. ตั้งค่า binding = null ใน onDestroyView()
  4. ไม่ใช้ getActivity().findViewById() ใน Fragment
  5. ใช้ requireView() (API 23+) ใน onViewCreated/onStart/onResume