Bắt hoạt động từ ngữ cảnh trong Android


184

Cái này làm tôi bí.

Tôi cần gọi một phương thức hoạt động từ trong một lớp bố trí tùy chỉnh. Vấn đề với điều này là tôi không biết cách truy cập hoạt động từ trong bố cục.

Hồ sơ xem

public class ProfileView extends LinearLayout
{
    TextView profileTitleTextView;
    ImageView profileScreenImageButton;
    boolean isEmpty;
    ProfileData data;
    String name;

    public ProfileView(Context context, AttributeSet attrs, String name, final ProfileData profileData)
    {
        super(context, attrs);
        ......
        ......
    }

    //Heres where things get complicated
    public void onClick(View v)
    {
        //Need to get the parent activity and call its method.
        ProfileActivity x = (ProfileActivity) context;
        x.activityMethod();
    }
}

Hồ sơ năng lực

public class ProfileActivityActivity extends Activity
{
    //In here I am creating multiple ProfileViews and adding them to the activity dynamically.

    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.profile_activity_main);
    }

    public void addProfilesToThisView()
    {
        ProfileData tempPd = new tempPd(.....)
        Context actvitiyContext = this.getApplicationContext();
        //Profile view needs context, null, name and a profileData
        ProfileView pv = new ProfileView(actvitiyContext, null, temp, tempPd);
        profileLayout.addView(pv);
    }
}

Như bạn có thể thấy ở trên, tôi đang khởi tạo profileView theo chương trình và truyền vào ActivityContext với nó. 2 câu hỏi:

  1. Tôi có chuyển đúng ngữ cảnh vào Profileview không?
  2. Làm cách nào để có được hoạt động chứa từ ngữ cảnh?

Câu trả lời:


473

Từ của bạn Activity, chỉ cần chuyển vào thislàm Contextbố cục của bạn:

ProfileView pv = new ProfileView(this, null, temp, tempPd);

Sau đó, bạn sẽ có một Contextbố cục, nhưng bạn sẽ biết nó thực sự là của bạn Activityvà bạn có thể sử dụng nó để bạn có những gì bạn cần:

Activity activity = (Activity) context;

53
Bạn không thể được đảm bảo rằng bối cảnh bạn đang làm việc là Bối cảnh hoạt động hoặc Bối cảnh ứng dụng. Hãy thử chuyển Bối cảnh ứng dụng sang DialogView, xem nó bị sập và bạn sẽ thấy sự khác biệt.
Sky Kelsey

6
Boris, câu hỏi hỏi liệu có cách nào để có được một Hoạt động từ một bối cảnh. Điều này là không thể. Tất nhiên bạn có thể đúc, nhưng đó là phương sách cuối cùng. Nếu bạn muốn coi Bối cảnh là một Hoạt động, thì đừng coi thường Hoạt động. Nó làm cho mã đơn giản hơn và ít bị lỗi sau này khi người khác đang duy trì mã của bạn.
Sky Kelsey

6
Lưu ý rằng 'getApplicationContext ()' thay vì 'this' sẽ không hoạt động.
dwbrito

1
@BorisStrandjev Tôi chưa hiểu bình luận của bạn. Dù sao, tôi đã nói rằng sau khi thử ví dụ của bạn nhưng thay vì 'this', tôi đã sử dụng getApplicationContext () và ứng dụng đã cố gắng tự tạo ứng dụng, do đó gây ra lỗi cast, thay vì hoạt động. Sau khi chuyển sang 'cái này', như bạn đã trả lời, nó đã hoạt động.
dwbrito

1
Các câu trả lời nâng cao nhất trên liên kết của bạn đều gợi ý thách thức câu hỏi nếu nó có mùi. Câu hỏi này chắc chắn là có mùi. Trước tiên, OP tuyên bố: "Tôi cần gọi một phương thức hoạt động từ bên trong một lớp bố cục tùy chỉnh." đó là hoàn toàn có thể đạt được với việc sử dụng giao diện thích hợp. Sau đó, anh ta nói "Vấn đề với điều này là tôi không biết cách truy cập hoạt động từ trong bố cục." đó là một gợi ý quan trọng đối với một sự hiểu lầm. Mọi người cố gắng làm điều sai trái mọi lúc trong lập trình và chúng ta không nên nhắm mắt làm ngơ.
Sam

39

Đây là thứ mà tôi đã sử dụng thành công để chuyển đổi Contextthành Activitykhi hoạt động trong giao diện người dùng theo phân đoạn hoặc chế độ xem tùy chỉnh. Nó sẽ giải nén ContextWrapper đệ quy hoặc trả về null nếu thất bại.

public Activity getActivity(Context context)
{
    if (context == null)
    {
        return null;
    }
    else if (context instanceof ContextWrapper)
    {
        if (context instanceof Activity)
        {
            return (Activity) context;
        }
        else
        {
            return getActivity(((ContextWrapper) context).getBaseContext());
        }
    }

    return null;
}

Đây là câu trả lời đúng. Những cái khác không tính đến hệ thống phân cấp ContentWrapper.
Snicolas

Đây là câu trả lời đúng :)
lygstate

1
@lygstate: Bạn đang sử dụng cấp API mục tiêu nào trong ứng dụng của mình? Lỗi là gì? Điều này chỉ hoạt động trong UI (hoạt động, đoạn, v.v.), không có trong Dịch vụ.
Theo

31
  1. Không
  2. Bạn không thể

Có hai bối cảnh khác nhau trong Android. Một cho ứng dụng của bạn (Hãy gọi nó là LỚN) và một cho mỗi chế độ xem (hãy gọi nó là bối cảnh hoạt động).

Một linearLayout là một khung nhìn, vì vậy bạn phải gọi bối cảnh hoạt động. Để gọi nó từ một hoạt động, chỉ cần gọi "này". Thật dễ dàng phải không?

Khi bạn sử dụng

this.getApplicationContext();

Bạn gọi bối cảnh LỚN, bối cảnh mô tả ứng dụng của bạn và không thể quản lý chế độ xem của bạn.

Một vấn đề lớn với Android là bối cảnh không thể gọi hoạt động của bạn. Đó là một vấn đề lớn để tránh điều này khi ai đó bắt đầu phát triển Android. Bạn phải tìm một cách tốt hơn để mã hóa lớp của mình (hoặc thay thế "Bối cảnh bối cảnh" bằng "Hoạt động hoạt động" và chuyển nó thành "Ngữ cảnh" khi cần).

Trân trọng.


Chỉ để cập nhật câu trả lời của tôi. Cách dễ nhất để có được của bạn Activity contextlà xác định một staticthể hiện trong Activity. Ví dụ

public class DummyActivity extends Activity
{
    public static DummyActivity instance = null;

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

        // Do some operations here
    }

    @Override
    public void onResume()
    {
        super.onResume();
        instance = this;
    }

    @Override
    public void onPause()
    {
        super.onPause();
        instance = null;
    }
}

Và sau đó, trong bạn Task, Dialog, View, bạn có thể sử dụng loại đó mã để có được của bạn Activity context:

if (DummyActivity.instance != null)
{
    // Do your operations with DummyActivity.instance
}

4
+1 để giải thích một khu vực nhầm lẫn rất phổ biến giữa 2 loại bối cảnh khác nhau (giống như có 2 loại khác nhau R). Google folks cần làm phong phú vốn từ vựng của họ.
an00b

3
BTW, @BorisStrandjev là chính xác: 2. Có, bạn có thể . (không thể tranh luận với mã làm việc)
an00b

2
2. Không thực sự. Nếu bối cảnh là bối cảnh Ứng dụng, thì ứng dụng của bạn sẽ bị sập.
StackOverflow

Ví dụ tĩnh?! @Nepster có giải pháp tốt nhất cho imo này
Sam

14
Tạo tham chiếu tĩnh cho Hoạt động là cách tốt nhất để tạo rò rỉ bộ nhớ.
BladeCoder

8

Nếu bạn muốn gọi một phương thức hoạt động từ trong một lớp bố cục tùy chỉnh (Lớp không hoạt động). Bạn nên tạo một đại biểu bằng giao diện.

Nó chưa được kiểm tra và tôi đã mã hóa nó đúng. nhưng tôi đang truyền đạt một cách để đạt được những gì bạn muốn.

Trước hết tạo và giao diện

interface TaskCompleteListener<T> {
   public void onProfileClicked(T result);
}



public class ProfileView extends LinearLayout
{
    private TaskCompleteListener<String> callback;
    TextView profileTitleTextView;
    ImageView profileScreenImageButton;
    boolean isEmpty;
    ProfileData data;
    String name;

    public ProfileView(Context context, AttributeSet attrs, String name, final ProfileData profileData)
    {
        super(context, attrs);
        ......
        ......
    }
    public setCallBack( TaskCompleteListener<String> cb) 
    {
      this.callback = cb;
    }
    //Heres where things get complicated
    public void onClick(View v)
    {
        callback.onProfileClicked("Pass your result or any type");
    }
}

Và thực hiện điều này cho bất kỳ hoạt động.

và gọi nó như

ProfileView pv = new ProfileView(actvitiyContext, null, temp, tempPd);
pv.setCallBack(new TaskCompleteListener
               {
                   public void onProfileClicked(String resultStringFromProfileView){}
               });

1
Đây là câu trả lời đúng và nên được đánh dấu là câu trả lời đúng. Tôi biết câu trả lời được đánh dấu là câu trả lời đúng thực sự trả lời câu hỏi của OP, nhưng không nên trả lời câu hỏi như vậy. Thực tế là nó không phải là một thực hành tốt để vượt qua Hoạt động như thế trong chế độ xem. Đứa trẻ không bao giờ nên biết về cha mẹ của chúng trong mọi trường hợp, ngoại trừ thông qua Context. Như Nepster tuyên bố, cách tốt nhất là chuyển qua một cuộc gọi lại, vì vậy bất cứ khi nào có điều gì đó xảy ra với phụ huynh, cuộc gọi lại sẽ được thực hiện với dữ liệu liên quan.
Darwind

6

Bối cảnh có thể là một Ứng dụng, Dịch vụ, Hoạt động và hơn thế nữa.

Thông thường, bối cảnh của Lượt xem trong một Hoạt động là chính Hoạt động nên bạn có thể nghĩ rằng bạn chỉ có thể truyền Bối cảnh này sang Hoạt động nhưng thực tế bạn không thể luôn làm như vậy, vì bối cảnh cũng có thể là ContextThemeWrapper trong trường hợp này.

ContextThemeWrapper được sử dụng rất nhiều trong các phiên bản gần đây của AppCompat và Android (nhờ thuộc tính android: theme trong bố cục) nên cá nhân tôi sẽ không bao giờ thực hiện dàn diễn viên này.

Vì vậy, câu trả lời ngắn gọn là: bạn không thể truy xuất một Hoạt động từ Ngữ cảnh trong Chế độ xem một cách đáng tin cậy. Truyền Activity cho khung nhìn bằng cách gọi một phương thức trên nó, lấy tham số Activity làm tham số.


3

Không bao giờ sử dụng getApplicationContext () với lượt xem.

Nó phải luôn luôn là bối cảnh của hoạt động, vì chế độ xem được gắn liền với hoạt động. Ngoài ra, bạn có thể có một bộ chủ đề tùy chỉnh và khi sử dụng ngữ cảnh của ứng dụng, tất cả các chủ đề sẽ bị mất. Đọc thêm về các phiên bản khác nhau của bối cảnh ở đây .


3

Và trong Kotlin:

tailrec fun Context.activity(): Activity? = when {
  this is Activity -> this
  else -> (this as? ContextWrapper)?.baseContext?.activity()
}

0

Hoạt động là một chuyên môn của Ngữ cảnh, vì vậy, nếu bạn có một Ngữ cảnh bạn đã biết hoạt động nào bạn định sử dụng và chỉ có thể chuyển a thành c ; trong đó a là một Hoạt động và c là một bối cảnh.

Activity a = (Activity) c;

7
Điều này nguy hiểm vì, như đã đề cập trong một nhận xét riêng biệt, bối cảnh có thể không phải lúc nào cũng là một Hoạt động.

4
typecast chỉ khi (hoạt động theo ngữ cảnh) {// typecast}
Amit Yadav

0

Tôi đã sử dụng chuyển đổi Hoạt động

Activity activity = (Activity) context;

2
Có nhiều loại bối cảnh khác nhau. Các hoạt động và ứng dụng có thể có bối cảnh. Điều này sẽ chỉ hoạt động khi bối cảnh là một hoạt động.
cylov

0

Phương pháp này sẽ hữu ích ..!

public Activity getActivityByContext(Context context){

if(context == null){
    return null;
    }

else if((context instanceof ContextWrapper) && (context instanceof Activity)){
        return (Activity) context;
    }

else if(context instanceof ContextWrapper){
        return getActivity(((ContextWrapper) context).getBaseContext());
    }

return null;

    }

Tôi hy vọng điều này sẽ giúp .. Mã hóa vui vẻ!


Kiểm tra xem bối cảnh bạn chuyển qua không phải là không .. Đó rất có thể là vấn đề.
Taslim Oseni

0

làm thế nào về một số cuộc gọi lại dữ liệu trực tiếp,

class ProfileView{
    private val _profileViewClicked = MutableLiveData<ProfileView>()
    val profileViewClicked: LiveData<ProfileView> = _profileViewClicked
}

class ProfileActivity{

  override fun onCreateView(...){

    profileViewClicked.observe(viewLifecycleOwner, Observer { 
       activityMethod()
    })
  }

}
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.