Lập trình lại / tái tạo một hoạt động?


121

Sau khi tôi thực hiện một số thay đổi trong cơ sở dữ liệu của mình, liên quan đến thay đổi quan trọng trong quan điểm của tôi, tôi muốn vẽ lại, thực hiện lại onCreate.

Làm thế nào là có thể?

Câu trả lời:


129

CẬP NHẬT : Android SDK 11 đã thêm một recreate()phương thức vào các hoạt động.


Tôi đã thực hiện điều đó bằng cách đơn giản sử dụng lại ý định bắt đầu hoạt động. Xác định một ý định starterIntenttrong lớp của bạn và chỉ định onCreate()sử dụng nó starterIntent = getIntent();. Sau đó, khi bạn muốn khởi động lại hoạt động, hãy gọifinish(); startActivity(starterIntent);

Đó không phải là một giải pháp rất thanh lịch, nhưng đó là một cách đơn giản để khởi động lại hoạt động của bạn và buộc nó tải lại mọi thứ.


8
thực sự tôi nghĩ rằng khi định hướng thiết bị thay đổi, hoạt động của nó được gọi theo cách tương tự .. vì vậy tôi không ngại làm điều này. startActivity (getIntent ()); hoàn thành();
Raja

Thật không thông minh khi thực hiện startActivity (getIntent ()) vì nó sẽ chỉ các hoạt động lớp trên đầu các hoạt động. Cần kết thúc hoạt động cũ
Fallenreaper

8
@Fallenreaper Tôi đề nghị gọi finish()ngay sau startActivity()đó vì lý do chính xác đó ...
Steve Haley

7
Tôi đạt được nó gọi đầu tiên finish();sau đóstartActivity(starterIntent);
Carlo Rodríguez

3
Vậy đó là cái gì? kết thúc () và sau đó startActivity? Hoặc cách khác xung quanh?
Jeff Padgett

92

Gọi phương thức tái tạo của hoạt động.


19
Điều này là ổn nếu ứng dụng của bạn chỉ nhắm mục tiêu SDK cấp 11 trở lên. Nếu không, tôi sẽ đi theo cách tiếp cận của Steve Haley.
TalkLittle

1
@FernandoEscher Thật không may, điều đó chỉ khả dụng trên các thiết bị Honeycomb trở lên.
IgorGanapolsky

2
nơi để gọi phương thức tái tạo
SAndroidD

1
tạo lại cuộc gọi phương thức được đóng lại một cách có nguồn gốc đóng ứng dụng
SAndroidD

Tôi đã từng sử dụng recreate()nhưng bây giờ tôi thấy một vấn đề kỳ lạ khi các nút radio không được đặt lại khi tạo lại, nhưng chúng thực hiện khi finish(); startActivity(getIntent());tôi đang sử dụng tính năng này bây giờ và xem nó hoạt động như thế nào trong những ngày hoặc tuần tiếp theo.
Ben

35

Kết hợp một số câu trả lời ở đây bạn có thể sử dụng một cái gì đó như sau.

class BaseActivity extends SherlockFragmentActivity
{
    // Backwards compatible recreate().
    @Override
    public void recreate()
    {
        if (android.os.Build.VERSION.SDK_INT >= 11)
        {
            super.recreate();
        }
        else
        {
            startActivity(getIntent());
            finish();
        }
    }
}

Kiểm tra

Tôi đã thử nghiệm nó một chút, và có một số vấn đề:

  1. Nếu hoạt động là mức thấp nhất trên ngăn xếp, việc gọi startActivity(...); finish();chỉ tồn tại ứng dụng và không khởi động lại hoạt động.
  2. super.recreate()không thực sự hoạt động theo cách tương tự như hoàn toàn tái tạo hoạt động. Nó tương đương với việc xoay thiết bị, vì vậy nếu bạn có bất kỳ Fragments nào vớisetRetainInstance(true) họ sẽ không được tái tạo; chỉ dừng lại và tiếp tục.

Vì vậy, hiện tại tôi không tin có một giải pháp chấp nhận được.


5
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMBthay vì sử dụng11
S.Thiongane

4
11 là nghiêm ngặt, .HONEYCOMB không đúng, vì mã của bạn trong SDK <11 không biết wtat là HONEYCOMB.
Tapa Lưu

6
thay thế startActivity(getIntent());finish();chofinish();startActivity(getIntent());
ahmed hamdy

Không, theo khuyến nghị, bạn nên nhắm mục tiêu sdk khả dụng cao nhất, vì vậy tổ ong sẽ có sẵn tại thời điểm biên dịch và hằng số int được mang trong ứng dụng của bạn. Tôi tin rằng đó là một mô hình phổ biến để sử dụng int sdk lớn hơn int sdk tối thiểu.
Hai Zhang

32

lựa chọn 1

Gọi recreate()về của bạn Activity. Tuy nhiên phương pháp này khiến màn hình đen nhấp nháy xuất hiện trong quá trình tạo lại hoạt động.

Lựa chọn 2

finish();
startActivity(getIntent());

Không có màn hình đen "nhấp nháy" ở đây, nhưng bạn sẽ thấy một sự chuyển đổi giữa các trường hợp cũ và mới với một nền đen không dễ chịu lắm. Chúng ta có thể làm tốt hơn.

Lựa chọn 3

Để khắc phục điều này, chúng tôi có thể thêm một cuộc gọi đến overridePendingTransition():

finish();
startActivity(getIntent());
overridePendingTransition(0, 0);

Tạm biệt màn hình đen, nhưng trong trường hợp của tôi, tôi vẫn thấy một loại chuyển tiếp (hoạt hình mờ dần), trên nền màu lần này. Đó là bởi vì bạn đang hoàn thành phiên bản hiện tại của hoạt động trước khi hoạt động mới được tạo và hiển thị đầy đủ và màu ở giữa là giá trị của windowBackgroundthuộc tính chủ đề.

Lựa chọn 4

startActivity(getIntent());
finish();

Gọi finish() sau startActivity() sẽ sử dụng chuyển đổi mặc định giữa các hoạt động, thường với một hoạt hình trượt nhỏ. Nhưng quá trình chuyển đổi vẫn có thể nhìn thấy.

Lựa chọn 5

startActivity(getIntent());
finish();
overridePendingTransition(0, 0);

Đối với tôi, đây là giải pháp tốt nhất vì nó khởi động lại hoạt động mà không có bất kỳ sự chuyển đổi rõ ràng nào, giống như nếu không có gì xảy ra.

Nó có thể hữu ích nếu, ví dụ, trong ứng dụng của bạn, bạn đưa ra một cách để thay đổi ngôn ngữ hiển thị độc lập với ngôn ngữ của hệ thống. Trong trường hợp này, bất cứ khi nào người dùng thay đổi ngôn ngữ của ứng dụng, bạn có thể muốn khởi động lại hoạt động của mình mà không cần chuyển đổi, làm cho việc chuyển đổi ngôn ngữ trở nên tức thời.


1
Một vấn đề với tùy chọn 5 là nó thêm hoạt động trước vào backstack. Gọi số này nhiều lần và người dùng của bạn phải bấm lại một loạt lần để đến trang thực sự trước đó.
Ollie

23

Khi tôi cần khởi động lại một hoạt động, tôi sử dụng mã sau đây. Mặc dù nó không được khuyến khích.

Intent intent = getIntent();
finish();
startActivity(intent);

1
Giải pháp rất sạch sẽ và thanh lịch. Hoạt động tuyệt vời trên 11 thiết bị tiền sdk.
IgorGanapolsky

Có vấn đề với phương thức super.recreate (), tuy nhiên cách này hoạt động tốt trên Lollipop

6

đối với API trước 11, bạn không thể sử dụng tái tạo (). Tôi đã giải quyết theo cách này:

Bundle temp_bundle = new Bundle();
onSaveInstanceState(temp_bundle);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("bundle", temp_bundle);
startActivity(intent);
finish();

và trong onCreate ..

@Override
public void onCreate(Bundle savedInstanceState) {

    if (getIntent().hasExtra("bundle") && savedInstanceState==null){
        savedInstanceState = getIntent().getExtras().getBundle("bundle");
    }

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //code

}

3

Sau khi tìm kiếm triển khai bánh gừng cho recreate, tôi muốn sử dụng các mã sau (cho bánh gừng):

activity.mMainThread.mAppThread.scheduleRelaunchActivity(activity.mToken, null, null, 0, false, null);

Đối với các mã này, đó là từ việc thực hiện trong api cao hơn.

public void recreate() {
    if (mParent != null) {
        throw new IllegalStateException("Can only be called on top-level activity");
    }
    if (Looper.myLooper() != mMainThread.getLooper()) {
        throw new IllegalStateException("Must be called from main thread");
    }
    mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, false);
}

Api-10 không có requestRelaunchActivity, tuy nhiên, từ diff, tôi đã tìm thấy điều này:

             public final void scheduleRelaunchActivity(IBinder token,
                     List<ResultInfo> pendingResults, List<Intent> pendingNewIntents,
                     int configChanges, boolean notResumed, Configuration config) {
    -            ActivityClientRecord r = new ActivityClientRecord();
    -
    -            r.token = token;
    -            r.pendingResults = pendingResults;
    -            r.pendingIntents = pendingNewIntents;
    -            r.startsNotResumed = notResumed;
    -            r.createdConfig = config;
    -
    -            synchronized (mPackages) {
    -                mRelaunchingActivities.add(r);
    -            }
    -
    -            queueOrSendMessage(H.RELAUNCH_ACTIVITY, r, configChanges);
    +            requestRelaunchActivity(token, pendingResults, pendingNewIntents,
    +                    configChanges, notResumed, config, true);
             }

Vì vậy, tôi nghĩ rằng tôi có thể sử dụng scheduleRelaunchActivitythay vì requestRelaunchActivity.

Và tôi đã viết chúng bằng phản ánh:

package me.piebridge.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.res.Configuration;
import android.os.Build;
import android.os.IBinder;

public class GingerBreadUtil {

    private static Field scanField(Class<?> clazz, String... names) {
        for (String name : names) {
            Field field;
            try {
                field = clazz.getDeclaredField(name);
                field.setAccessible(true);
                return field;
            } catch (NoSuchFieldException e) {
            }
            try {
                field = clazz.getField(name);
                field.setAccessible(true);
                return field;
            } catch (NoSuchFieldException e) {
            }
        }
        return null;
    }

    public static void recreate(Activity activity) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
            recreateHC(activity);
        } else {
            try {
                recreateGB(activity);
            } catch (InvocationTargetException e) {
                e.getTargetException().printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private static void recreateHC(Activity activity) {
        ((Activity) activity).recreate();
    }

    private static void recreateGB(Activity activity) throws IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Field Activity$mToken = scanField(Activity.class, "mToken");
        IBinder mToken = (IBinder) Activity$mToken.get(activity);
        Field Activity$mMainThread = scanField(Activity.class, "mMainThread");
        Object mMainThread = Activity$mMainThread.get(activity);
        Field ActivityThread$mAppThread = scanField(mMainThread.getClass(), "mAppThread");
        Object mAppThread = ActivityThread$mAppThread.get(mMainThread);
        Method method = mAppThread.getClass().getMethod("scheduleRelaunchActivity",
            IBinder.class, List.class, List.class, int.class, boolean.class, Configuration.class);
        method.invoke(mAppThread, mToken, null, null, 0, false, null);
    }

}

Tôi đang sử dụng các mã này để chuyển ngược lại khung xposed.


Công việc thú vị! Tôi đã thử nghiệm trong một trình giả lập và cách tiếp cận này tương thích ngược với Build.VERSION_CODES.ECLAIR_MR1(v7). Nó có thể hoạt động trên các phiên bản cũ là tốt.
Tim Cooke

3

Gọi recreate() phương thức từ nơi bạn muốn tạo lại hoạt động của mình. Phương thức này sẽ hủy phiên bản hiện tại của Activity onDestroy()và sau đó tạo lại hoạt động với onCreate().


1

Nếu đây là vấn đề của bạn, có lẽ bạn nên thực hiện một cách khác để thực hiện chế độ xem điền vào Hoạt động của mình. Thay vì chạy lại, onCreate()bạn nên làm cho nó onCreate()gọi phương thức điền của bạn với một số đối số. Khi dữ liệu thay đổi, phương thức điền sẽ được gọi với một đối số khác.


1

Cách tôi giải quyết là bằng cách sử dụng Fragment . Chúng tương thích ngược cho đến API 4 bằng cách sử dụng thư viện hỗ trợ.

Bạn tạo bố cục "trình bao bọc" với FrameLayout trong đó.

Thí dụ:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical" >

     <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/fragment_container"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
</LinearLayout>

Sau đó, bạn tạo một FragmentActivity trong đó bạn có thể thay thế FrameLayout bất cứ lúc nào bạn muốn.

Thí dụ:

public class SampleFragmentActivity extends FragmentActivity
{

     @Override
 public void onCreate(Bundle savedInstanceState)
 {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.wrapper);

    // Check that the activity is using the layout version with
    // the fragment_container FrameLayout
    if (findViewById(R.id.fragment_container) != null)
    {

        // However, if we're being restored from a previous state,
        // then we don't need to do anything and should return or else
        // we could end up with overlapping fragments.
        if (savedInstanceState != null)
        {
            return;
        }
        updateLayout();
     }
  }

  private void updateLayout()
  {
     Fragment fragment = new SampleFragment();
     fragment.setArguments(getIntent().getExtras());

     // replace original fragment by new fragment
     getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment).commit();
  }

Trong Fragment bạn thổi phồng / thay thế, bạn có thể sử dụng onStart và onCreateView giống như bạn Normaly sẽ sử dụng onCreate của một hoạt động.

Thí dụ:

public class SampleFragment extends Fragment
{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.yourActualLayout, container, false);
    }

    @Override
    public void onStart()
    {
        // do something with the components, or not!
        TextView text = (TextView) getActivity().findViewById(R.id.text1);

        super.onStart();
    }
}

1

Cũng tùy thuộc vào tình huống của bạn, bạn có thể cần getActivity().recreate();thay vì chỉ recreate().

Ví dụ, bạn nên sử dụng nó nếu bạn đang làm recreate()trong lớp đã được tạo bên trong lớp hoạt động.


0

Tôi đã từng tạo một ứng dụng thử nghiệm tải lên, xóa và sau đó tải lại tệp cơ sở dữ liệu bằng cách sử dụng lưu trữ đám mây firebase. Để hiển thị dữ liệu trong cơ sở dữ liệu, đoạn mã sau là giải pháp duy nhất tôi tìm thấy. Không recreate()phải cũng không finish()làm việc trong trường hợp này.

Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
System.exit(0);

0

tôi đã tìm ra cách tốt nhất để làm mới Fragment của bạn khi dữ liệu thay đổi

nếu bạn có nút "tìm kiếm", bạn phải khởi tạo danh sách ARRAY của mình bên trong nút

mSearchBtn.setOnClickListener (Chế độ xem mới.OnClickListener () {

@Override void công khai trênClick (Xem v) {

mList = new ArrayList<Node>();

firebaseSearchQuery.addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {


      for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {

        Node p = dataSnapshot1.getValue(Node .class);
        mList.add(p);
      }
      YourAdapter = new NodeAdapter(getActivity(), mList);
      mRecyclerView.setAdapter(YourAdapter );

    }

-2

Nếu bạn muốn truyền tham số cho onCreate () thì bạn phải tạo một ý định mới bằng cách thêm phụ và gọi StartActivity với nó. Đây là một ví dụ đơn giản mà tôi đã sử dụng theo cách này.

              String eczSabit = sa.getItem(position).getValue();
              if(!Util.IsNullOrEmpty(eczSabit)){
                  sabit = Long.parseLong(eczSabit);
                  Intent intent = new Intent(eczaneSegmentasyon.this,eczaneSegmentasyon.class);
                  intent.putExtra("sabit", sabit);
                  startActivity(intent);
              }

quy ước đặt tên xấu, tên biến xấu, mã thực sự khó hiểu ... -1
Ahmed Adel Ismail

-4

Nếu bạn chỉ muốn làm lại quan điểm của mình, tôi đã có cùng một vấn đề. Trong onResumehàm thử đặt cái này:

mView = new AndroidPinballView(getApplication());

Điều này cũng là của tôi onCreate(), vì vậy việc đưa nó vào onResumelàm việc cho tôi :)

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.