Làm cách nào để tôi thêm một đoạn vào Hoạt động với chế độ xem nội dung được lập trình


240

Tôi muốn thêm một đoạn vào một Hoạt động thực hiện bố cục của nó theo chương trình. Tôi đã xem qua tài liệu Fragment nhưng không có nhiều ví dụ mô tả những gì tôi cần. Đây là loại mã tôi đã cố gắng viết:

public class DebugExampleTwo extends Activity {

    private ExampleTwoFragment mFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FrameLayout frame = new FrameLayout(this);
        if (savedInstanceState == null) {
            mFragment = new ExampleTwoFragment();
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.add(frame.getId(), mFragment).commit();
        }

        setContentView(frame);
    }
}

...

public class ExampleTwoFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, 
                             ViewGroup container, 
                             Bundle savedInstanceState) {
        Button button = new Button(getActivity());
        button.setText("Hello There");
        return button;
    }
}

Mã này biên dịch nhưng gặp sự cố khi bắt đầu, có thể là do tôi FragmentTransaction.add()không chính xác. cách chính xác để làm điều này là gì?

Câu trả lời:


198

Hóa ra có nhiều hơn một vấn đề với mã đó. Một đoạn không thể được khai báo theo cách đó, bên trong cùng một tệp java với hoạt động nhưng không phải là một lớp bên trong công khai. Khung này dự kiến ​​hàm tạo của đoạn (không có tham số) sẽ được công khai và hiển thị. Di chuyển đoạn vào Hoạt động như một lớp bên trong hoặc tạo tệp java mới cho đoạn đó sửa lỗi đó.

Vấn đề thứ hai là khi bạn thêm một đoạn theo cách này, bạn phải chuyển một tham chiếu đến chế độ xem chứa của đoạn đó và chế độ xem đó phải có id tùy chỉnh. Sử dụng id mặc định sẽ làm hỏng ứng dụng. Đây là mã được cập nhật:

public class DebugExampleTwo extends Activity {

    private static final int CONTENT_VIEW_ID = 10101010;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FrameLayout frame = new FrameLayout(this);
        frame.setId(CONTENT_VIEW_ID);
        setContentView(frame, new LayoutParams(
            LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

        if (savedInstanceState == null) {
            Fragment newFragment = new DebugExampleTwoFragment();
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.add(CONTENT_VIEW_ID, newFragment).commit();
        }
    }

    public static class DebugExampleTwoFragment extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            EditText v = new EditText(getActivity());
            v.setText("Hello Fragment!");
            return v;
        }
    }
}

115
Nếu bạn chỉ muốn sử dụng đoạn này làm chế độ xem nội dung cấp cao nhất của hoạt động, thì bạn có thể sử dụng ft.add(android.R.id.content, newFragment). Chỉ cần tạo bố cục tùy chỉnh và đặt id của nó nếu vùng chứa của đoạn không phải là chế độ xem nội dung của hoạt động.
Tony Wong

25
Thay vì mã hóa cứng id, bạn có thể định nghĩa nó bằng XML và tham chiếu nó như bình thường (R.id.myid).
Jason Hanley

1
Tôi không biết làm thế nào để làm điều đó, nhưng hãy nhớ rằng một id chỉ phải là duy nhất trong phạm vi mà bạn cần sử dụng nó.
Jason Hanley

2
id chỉ cần là duy nhất ở cấp độ của nó trong chế độ thừa kế hiện tại của bố cục chứa. Vì vậy, nói rằng nó được bao bọc trong một bố cục tuyến tính, nó chỉ cần không có giá trị trong số các chế độ xem khác trong bố cục tuyến tính đó.
Shaun

1
Bạn có thể tạo ID động bằng cách sử dụng setId (View.NO_ID) và sau đó getId () để xem nó là gì.
ext8enmentnow

71

Đây là những gì tôi nghĩ ra sau khi đọc bình luận của Tony Wong :

public class DebugExampleTwo extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addFragment(android.R.id.content,
                    new DebugExampleTwoFragment(),
                    DebugExampleTwoFragment.FRAGMENT_TAG);
    }

}

...

public abstract class BaseActivity extends Activity {

    protected void addFragment(@IdRes int containerViewId,
                               @NonNull Fragment fragment,
                               @NonNull String fragmentTag) {
        getSupportFragmentManager()
                .beginTransaction()
                .add(containerViewId, fragment, fragmentTag)
                .disallowAddToBackStack()
                .commit();
    }

    protected void replaceFragment(@IdRes int containerViewId,
                                   @NonNull Fragment fragment,
                                   @NonNull String fragmentTag,
                                   @Nullable String backStackStateName) {
        getSupportFragmentManager()
                .beginTransaction()
                .replace(containerViewId, fragment, fragmentTag)
                .addToBackStack(backStackStateName)
                .commit();
    }

}

...

public class DebugExampleTwoFragment extends Fragment {

    public static final String FRAGMENT_TAG = 
        BuildConfig.APPLICATION_ID + ".DEBUG_EXAMPLE_TWO_FRAGMENT_TAG";

    // ...

}

Kotlin

Nếu bạn đang sử dụng Kotlin, hãy đảm bảo xem các tiện ích mở rộng của Kotlin do Google cung cấp hoặc chỉ viết riêng cho bạn.


Đừng làm như vậy! Kiểm tra if (savedInstanceState == null)trước khi tạo mảnh vỡ hoặc sau khi xoay màn hình, bạn sẽ có hai mảnh vỡ hoặc mảnh vỡ sắp xếp lại. Không sử dụng addphương pháp nào cả! Chỉ replace. Hoặc bạn sẽ có hành vi kỳ lạ.
CoolMind

Nơi nào bạn nhận được giá trị cho "backStackStateName"? (Khi sử dụng chức năng thay thế)
vikzilla

@vikzilla Bạn có thể tìm thấy câu trả lời khá hay ở đây và trong các tài liệu . Tóm lại: backStackStateNamechuỗi là một cái gì đó được xác định bởi bạn.
JJD

34
    public class Example1 extends FragmentActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
          DemoFragment fragmentDemo = (DemoFragment) 
          getSupportFragmentManager().findFragmentById(R.id.frame_container);
          //above part is to determine which fragment is in your frame_container
          setFragment(fragmentDemo);
                       (OR)
          setFragment(new TestFragment1());
        }

        // This could be moved into an abstract BaseActivity 
        // class for being re-used by several instances
        protected void setFragment(Fragment fragment) {
            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = 
                fragmentManager.beginTransaction();
            fragmentTransaction.replace(android.R.id.content, fragment);
            fragmentTransaction.commit();
        }
    }

Để thêm một đoạn vào Hoạt động hoặc FramentActivity, nó cần có Container. Container đó phải là " Framelayout", có thể được bao gồm trong xml hoặc nếu không bạn có thể sử dụng vùng chứa mặc định cho "like android.R.id.content" để loại bỏ hoặc thay thế một đoạn trong Activity.

tệp chính

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 <!-- Framelayout to display Fragments -->
   <FrameLayout
        android:id="@+id/frame_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ImageView
        android:id="@+id/imagenext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_margin="16dp"
        android:src="@drawable/next" />
</RelativeLayout>

29

Sau khi đọc tất cả các câu trả lời tôi đã đưa ra một cách thanh lịch:

public class MyActivity extends ActionBarActivity {

 Fragment fragment ;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    FragmentManager fm = getSupportFragmentManager();
    fragment = fm.findFragmentByTag("myFragmentTag");
    if (fragment == null) {
        FragmentTransaction ft = fm.beginTransaction();
        fragment =new MyFragment();
        ft.add(android.R.id.content,fragment,"myFragmentTag");
        ft.commit();
    }

}

về cơ bản, bạn không cần thêm frameLayout dưới dạng vùng chứa của đoạn thay vào đó, bạn có thể thêm đoạn thẳng vào vùng chứa gốc của Android View

QUAN TRỌNG: không sử dụng thay thế đoạn như hầu hết các phương pháp này đưa ra ở đây, trừ khi bạn không nhớ để mất trạng thái biến Ví dụ đoạn trong onrecreation quá trình.


cảm ơn câu trả lời, điều này thêm tab mảnh cho toàn màn hình? Nhưng làm thế nào bạn thêm vào một bố cục khung hoặc xem máy nhắn tin?
flankechen

6
public abstract class SingleFragmentActivity extends Activity {

    public static final String FRAGMENT_TAG = "single";
    private Fragment fragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
        if (savedInstanceState == null) {
            fragment = onCreateFragment();
           getFragmentManager().beginTransaction()
                   .add(android.R.id.content, fragment, FRAGMENT_TAG)
                   .commit();
       } else {
           fragment = getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
       }
   }

   public abstract Fragment onCreateFragment();

   public Fragment getFragment() {
       return fragment;
   }

}

sử dụng

public class ViewCatalogItemActivity extends SingleFragmentActivity {
    @Override
    public Fragment onCreateFragment() {
        return new FragmentWorkShops();
    }

}

6

Đối với API cấp 17 trở lên, View.generateViewId()sẽ giải quyết vấn đề này. Phương thức tiện ích cung cấp một id duy nhất không được sử dụng trong thời gian xây dựng.


3
Chào mừng bạn đến với Stack Overflow! Trong khi điều này về mặt lý thuyết có thể trả lời câu hỏi, tốt hơn là nên bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo.
SuperBiasedMan

3

Để đính kèm đoạn vào một hoạt động được lập trình trong Kotlin, bạn có thể xem đoạn mã sau:

ChínhActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

            // create fragment instance
            val fragment : FragmentName = FragmentName.newInstance()

            // for passing data to fragment
            val bundle = Bundle()
            bundle.putString("data_to_be_passed", DATA)
            fragment.arguments = bundle

            // check is important to prevent activity from attaching the fragment if already its attached
            if (savedInstanceState == null) {
                supportFragmentManager
                    .beginTransaction()
                    .add(R.id.fragment_container, fragment, "fragment_name")
                    .commit()
            }
        }

    }
}

Activity_main.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"
    tools:context=".ui.MainActivity">

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

FragmentName.kt

class FragmentName : Fragment() {

    companion object {
        fun newInstance() = FragmentName()
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        // receiving the data passed from activity here
        val data = arguments!!.getString("data_to_be_passed")
        return view
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
    }

}

Nếu bạn đã quen thuộc với Extensions trong Kotlin sau đó bạn thậm chí có thể tốt hơn mã này bằng cách làm theo này bài viết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.