thanh hành động lên điều hướng với các đoạn


88

Tôi có một Tabbed Actionbar / ViewPager bố trí với ba tab nói Một , B , và C . Trong tab C tab (fragment), Tôi thêm một tiếng nói mảnh mảnh D . với

 DFragment f= new DFragment();
 ft.add(android.R.id.content, f, "");
 ft.remove(CFragment.this);
 ft.addToBackStack(null);
 ft.commit();

Tôi sửa đổi thanh hành động trong onResume của DFragment để thêm nút:

ActionBar ab = getActivity().getActionBar();
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);

Bây giờ trong DFragment, khi tôi nhấn nút Quay lại phần cứng (điện thoại), tôi quay lại bố cục Tabbed (ABC) ban đầu với CFragment được chọn. Làm cách nào để đạt được chức năng này với nút lên trên thanh tác vụ?


Câu trả lời:


185

Triển khai OnBackStackChangedListenervà thêm mã này vào Hoạt động phân mảnh của bạn.

@Override
public void onCreate(Bundle savedInstanceState) {
    //Listen for changes in the back stack
    getSupportFragmentManager().addOnBackStackChangedListener(this);
    //Handle when activity is recreated like on orientation Change
    shouldDisplayHomeUp();
}

@Override
public void onBackStackChanged() {
    shouldDisplayHomeUp();
}

public void shouldDisplayHomeUp(){
   //Enable Up button only  if there are entries in the back stack
   boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
   getSupportActionBar().setDisplayHomeAsUpEnabled(canGoBack);
}

@Override
public boolean onSupportNavigateUp() {
    //This method is called when the up button is pressed. Just the pop back stack.
    getSupportFragmentManager().popBackStack();
    return true;
}

20
Giới thiệu onSupportNavigateUp(), "Phương thức không ghi đè phương thức từ lớp cha của nó".
Fred

10
Nếu bạn đã có onOptionsItemSelected, bạn cũng có thể kiểm tra itemId android.R.id.homethay vì thêm onSupportNavigateUp.
domsom

1
Nếu phiên bản API> = 14, Sử dụng onNavigateUp thay vì onSupportNavigateUp @Override boolean công khai onNavigateUp () {// Phương thức này được gọi khi nhấn nút lên. Chỉ là ngăn xếp bật lại. getFragmentManager (). popBackStack (); trả về true; }
Tejasvi Hegde

1
Có phải có một dấu mũ lên hiển thị bên cạnh biểu tượng ứng dụng của bạn trong ActionBar? Tôi không thấy một khi tôi triển khai mã này. Tôi chỉ có thể nhấp vào biểu tượng, nhưng nó không làm gì cả. Android 4.0+.
Azurespot

3
Nếu onBackStackChanged () không ghi đè, hãy đảm bảo hoạt động của bạn triển khai giao diện FragmentManager.OnBackStackChangedListener.
CBA110

41

Tôi hiểu rồi. chỉ cần ghi đè onOptionsItemSelected trong hoạt động lưu trữ và bật lên backstack, ví dụ:

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home: {
            FragmentManager fm = getSupportFragmentManager();
            if (fm.getBackStackEntryCount() > 0) {
                fm.popBackStack();
                return true;
            }
            break;
        }
    }
    return super.onOptionsItemSelected(item);
}

Gọi getActionBar().setDisplayHomeAsUpEnabled(boolean);getActionBar().setHomeButtonEnabled(boolean);đến onBackStackChanged()như được giải thích trong câu trả lời bên dưới.


3
bạn cũng phải gọi getActivity (). getActionBar (). setDisplayHomeAsUpEnabled (false); để loại bỏ các nút lên khi bạn bật phía sau chồng
Jop

Đây không phải là cách chính xác để làm điều này. Nó tiếp tục bật nút lên.
Roger Garzon Nieto

1
Bạn nên đặt mã đó bên trong switchcâu lệnh với trường hợp android.R.id.home.
Fred

18

Nếu bạn có một hoạt động của cha mẹ và muốn nút lên này hoạt động như một nút quay lại, bạn có thể sử dụng mã này:

thêm cái này vào onCreate trong lớp hoạt động chính của bạn

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            int stackHeight = getSupportFragmentManager().getBackStackEntryCount();
            if (stackHeight > 0) { // if we have something on the stack (doesn't include the current shown fragment)
                getSupportActionBar().setHomeButtonEnabled(true);
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            } else {
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                getSupportActionBar().setHomeButtonEnabled(false);
            }
        }

    });

và sau đó thêm onOptionsItemSelected như sau:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            getSupportFragmentManager().popBackStack();
            return true;
     ....
 }

Tôi thường sử dụng cái này mọi lúc và có vẻ khá hợp pháp


1
Tôi đã thử sử dụng chính xác mã này, theo mã Activitycủa tôi, với hy vọng rằng nó sẽ quay trở lại phân đoạn mà nó bắt đầu. Nút quay lại xuất hiện gần biểu tượng ứng dụng của tôi khi tôi truy cập vào của tôi Activity, nhưng tôi nhấp vào biểu tượng và không có gì xảy ra (nó sẽ quay trở lại phân đoạn). Bất kỳ ý tưởng tại sao? Cảm ơn.
Azurespot

1
@Daniel mã của bạn là hợp pháp .. nó hoạt động. Bạn chỉ có thể muốn sorround nó với tùy chọn catch chỉ trong trường hợp .. bạn biết để ngăn chặn bất kỳ trường hợp ngoại lệ không lường trước được và ứng dụng đâm
Zuko

1
@NoniA. Thao tác này chỉ quay trở lại phân đoạn trước đó (ví dụ: phân đoạn B -> phân đoạn A), nếu bạn đang thổi phồng 1 phân đoạn trong một hoạt động mới, điều này sẽ không quay trở lại hoạt động trước đó.
Daniel Jonker

10

bạn có thể quay lại bằng nút lên như nút quay lại;

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            super.onBackPressed();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

8

Tôi biết câu hỏi này đã cũ, nhưng có thể ai đó (như tôi) cũng cần nó.

Nếu Hoạt động của bạn mở rộng AppCompatActivity , bạn có thể sử dụng giải pháp đơn giản hơn (hai bước):

1 - Bất cứ khi nào bạn thêm một phân đoạn không phải trang chủ, chỉ cần hiển thị nút lên, ngay sau khi thực hiện giao dịch phân đoạn. Như thế này:

    // ... add a fragment
    // Commit the transaction
    transaction.commit();

    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

2 - Sau đó, khi nút UP được nhấn, bạn ẩn nó đi.

@Override
public boolean onSupportNavigateUp() {
    getSupportActionBar().setDisplayHomeAsUpEnabled(false);        
    return true;
}

Đó là nó.


7

Tôi đã sử dụng kết hợp các câu trả lời của Roger Garzon Nietosohailaziz . Ứng dụng của tôi có một MainActivity duy nhất và các đoạn A, B, C được tải vào đó. Đoạn "nhà" của tôi (A) triển khai OnBackStackChangedListener và kiểm tra kích thước của backStack; nếu nó nhỏ hơn một, thì nó sẽ ẩn nút LÊN. Phân đoạn B và C luôn tải nút quay lại (trong thiết kế của tôi, B được khởi chạy từ A, và C được khởi chạy từ B). Bản thân MainActivity chỉ bật ra backstack khi chạm vào nút LÊN và có các phương pháp để hiển thị / ẩn nút mà các mảnh gọi là:

Hoạt động chủ yêu:

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        // Respond to the action bar's Up/Home button
        case android.R.id.home:
            getSupportFragmentManager().popBackStack();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

public void showUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(true); }
public void hideUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(false); }

mảnhA (triển khai FragmentManager.OnBackStackChangedListener):

public void onCreate(Bundle savedinstanceSate) {
    // listen to backstack changes
    getActivity().getSupportFragmentManager().addOnBackStackChangedListener(this);

    // other fragment init stuff
    ...
}

public void onBackStackChanged() {
    // enable Up button only  if there are entries on the backstack
    if(getActivity().getSupportFragmentManager().getBackStackEntryCount() < 1) {
        ((MainActivity)getActivity()).hideUpButton();
    }
}

mảnhB, mảnhC:

public void onCreate(Bundle savedinstanceSate) {
    // show the UP button
    ((MainActivity)getActivity()).showUpButton();

    // other fragment init stuff
    ...
}

5

Điều này đã làm việc cho tôi. Ghi đè onSupportNavigateUp và onBackPressed, chẳng hạn (mã trong Kotlin);

override fun onBackPressed() {
    val count = supportFragmentManager.backStackEntryCount
    if (count == 0) {
        super.onBackPressed()
    } else {
        supportFragmentManager.popBackStack()
    }
}

override fun onSupportNavigateUp(): Boolean {
    super.onSupportNavigateUp()
    onBackPressed()
    return true
}

Bây giờ trong phân đoạn, nếu bạn hiển thị mũi tên lên

activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)

Nhấp vào nó sẽ đưa bạn trở lại hoạt động trước đó.


5

Kotlin:

class MyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        supportFragmentManager.addOnBackStackChangedListener { setupHomeAsUp() }
        setupHomeAsUp()
    }

    private fun setupHomeAsUp() {
        val shouldShow = 0 < supportFragmentManager.backStackEntryCount
        supportActionBar?.setDisplayHomeAsUpEnabled(shouldShow)
    }

    override fun onSupportNavigateUp(): Boolean = 
        supportFragmentManager.popBackStack().run { true }

    ...
}

2

Đây là một giải pháp rất tốt và đáng tin cậy: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragment/

Anh chàng đã tạo một đoạn trừu tượng xử lý hành vi backPress và đang chuyển đổi giữa các đoạn hoạt động bằng cách sử dụng mẫu chiến lược.

Đối với một số bạn, có thể có một chút hạn chế trong lớp trừu tượng ...

Trong thời gian ngắn, giải pháp từ liên kết sẽ như sau:

// Abstract Fragment handling the back presses

public abstract class BackHandledFragment extends Fragment {
    protected BackHandlerInterface backHandlerInterface;
    public abstract String getTagText();
    public abstract boolean onBackPressed();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(!(getActivity()  instanceof BackHandlerInterface)) {
            throw new ClassCastException("Hosting activity must implement BackHandlerInterface");
        } else {
            backHandlerInterface = (BackHandlerInterface) getActivity();
        }
    }

    @Override
    public void onStart() {
        super.onStart();

        // Mark this fragment as the selected Fragment.
        backHandlerInterface.setSelectedFragment(this);
    }

    public interface BackHandlerInterface {
        public void setSelectedFragment(BackHandledFragment backHandledFragment);
    }
}   

Và cách sử dụng trong hoạt động:

// BASIC ACTIVITY CODE THAT LETS ITS FRAGMENT UTILIZE onBackPress EVENTS 
// IN AN ADAPTIVE AND ORGANIZED PATTERN USING BackHandledFragment

public class TheActivity extends FragmentActivity implements BackHandlerInterface {
    private BackHandledFragment selectedFragment;

    @Override
    public void onBackPressed() {
        if(selectedFragment == null || !selectedFragment.onBackPressed()) {
            // Selected fragment did not consume the back press event.
            super.onBackPressed();
        }
    }

    @Override
    public void setSelectedFragment(BackHandledFragment selectedFragment) {
        this.selectedFragment = selectedFragment;
    }
}

Mặc dù liên kết này có thể trả lời câu hỏi, nhưng tốt hơn hết bạn nên đưa các phần thiết yếu của câu trả lời vào đây và cung cấp liên kết để tham khảo. Các câu trả lời chỉ có liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi.
bummi

BTW: nếu bạn xác định các bản sao, hãy gắn cờ chúng như vậy. cám ơn.
bummi

điều quan trọng là setSelectedFragment bên trong onStart?
VLeonovs
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.