Uzyskiwanie aktywności z kontekstu w Androidzie

184

Ten mnie zaskoczył.

Muszę wywołać metodę działania z niestandardowej klasy układu. Problem polega na tym, że nie wiem, jak uzyskać dostęp do działania z poziomu układu.

Widok profilu

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();
    }
}

ProfileActivity

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);
    }
}

Jak widać powyżej, tworzę programowo instancję ProfileView i przekazuję z nią działanie ActivityContext. 2 pytania:

  1. Czy przekazuję poprawny kontekst do Profileview?
  2. Jak uzyskać aktywność zawierającą z kontekstu?
OVERTONE
źródło

Odpowiedzi:

472

Po Activityprostu podaj thisjako Contextswój układ:

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

Następnie będziesz mieć Contextukład, ale będziesz wiedział, że to rzeczywiście twój Activityi możesz go rzucić, aby mieć to, czego potrzebujesz:

Activity activity = (Activity) context;
Boris Strandjev
źródło
53
Nie można zagwarantować, że kontekstem, z którym pracujesz, jest kontekst działania lub kontekst aplikacji. Spróbuj przekazać kontekst aplikacji do okna dialogowego DialogView, zobacz, jak się zawiesza, a zobaczysz różnicę.
Sky Kelsey
6
Boris, pytanie dotyczy tego, czy istnieje sposób na uzyskanie działania z kontekstu. To jest niemożliwe. Oczywiście możesz rzucać, ale to jest ostateczność. Jeśli chcesz traktować kontekst jako działanie, nie sprowadzaj się do działania. Ułatwia to kod i jest mniej podatny na błędy później, gdy inna osoba utrzymuje Twój kod.
Sky Kelsey,
6
Zauważ, że „getApplicationContext ()” zamiast „this” nie będzie działać.
dwbrito
1
@BorisStrandjev Nie do końca rozumiem twój komentarz. W każdym razie powiedziałem, że po wypróbowaniu twojego przykładu, ale zamiast „tego” użyłem getApplicationContext (), a aplikacja próbowała rzutować samą aplikację, co spowodowało błąd rzutowania zamiast działania. Po przełączeniu na „to”, jak odpowiedziałeś, zadziałało.
dwbrito
1
Obie najwyżej ocenione odpowiedzi w twoim linku sugerują kwestionowanie pytania, czy jest śmierdzące. To pytanie z pewnością śmierdzi. PO najpierw stwierdził: „Muszę wywołać metodę działania z niestandardowej klasy układu”. co jest całkowicie możliwe do osiągnięcia przy odpowiednim użyciu interfejsów. Następnie mówi „Problem polega na tym, że nie wiem, jak uzyskać dostęp do działania z poziomu układu”. co jest znaczącą wskazówką do nieporozumienia. Ludzie cały czas próbują robić złe rzeczy w programowaniu i nie powinniśmy przymykać na to oczu.
Sam
39

To jest coś, co z powodzeniem stosowany do konwersji Context, aby Activitypodczas pracy w interfejsie we fragmentach lub widoków niestandardowych. Rozpakuje rekursywnie ContextWrapper lub zwróci null, jeśli się nie powiedzie.

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;
}
Theo
źródło
To jest właściwa odpowiedź. Pozostałe nie uwzględniają hierarchii ContentWrapper.
Snicolas,
To jest prawdziwa odpowiedź :)
ligstate
1
@lygstate: Jakiego docelowego poziomu interfejsu API używasz w swojej aplikacji? Jaki jest błąd? Działa to tylko w interfejsie użytkownika (działania, fragmenty itp.), A nie w usługach.
Theo
31
  1. Nie
  2. Nie możesz

W Androidzie istnieją dwa różne konteksty. Jeden dla Twojej aplikacji (nazwijmy to DUŻYM) i jeden dla każdego widoku (nazwijmy to kontekstem działania).

Liniowy układ jest widokiem, więc musisz wywołać kontekst działania. Aby nazwać to z działania, po prostu nazywaj to. To takie proste, prawda?

Kiedy używasz

this.getApplicationContext();

Wywołujesz kontekst BIG, ten, który opisuje twoją aplikację i nie może zarządzać twoim widokiem.

Dużym problemem związanym z Androidem jest to, że kontekst nie może wywołać Twojej aktywności. To wielka sprawa, aby tego uniknąć, gdy ktoś zaczyna od opracowania Androida. Musisz znaleźć lepszy sposób na zakodowanie swojej klasy (lub zastąp „Kontekst kontekstowy” przez „Aktywność aktywności” i w razie potrzeby przenieś go na „Kontekst”).

Pozdrowienia.


Aby zaktualizować moją odpowiedź. Najłatwiejszym sposobem na uzyskanie tego Activity contextjest zdefiniowanie staticinstancji w twoim Activity. Na przykład

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

A potem, w twoich Task, Dialog, View, można użyć tego rodzaju kodu, aby otrzymać Activity context:

if (DummyActivity.instance != null)
{
    // Do your operations with DummyActivity.instance
}
Manitoba
źródło
4
+1 za wyjaśnienie bardzo powszechnego obszaru nieporozumień między 2 różnymi typami kontekstów (tak jak są 2 różne Rs). Ludzie Google muszą wzbogacić swoje słownictwo.
an00b
3
BTW, @BorisStrandjev jest poprawny: 2. Tak, możesz . (nie może kłócić się z działającym kodem)
an00b
2
2. Nie bardzo. Jeśli kontekstem był kontekst aplikacji, aplikacja ulegnie awarii.
StackOverflow Flow
instancja statyczna ?! @Nepster ma najlepsze rozwiązanie tego imo
Sam
14
Tworzenie statycznego odniesienia do działania jest najlepszym sposobem na tworzenie wycieków pamięci.
BladeCoder,
8

Jeśli chcesz wywołać metodę działania z niestandardowej klasy układu (innej niż klasa aktywności). Powinieneś utworzyć delegata za pomocą interfejsu.

Jest nieprzetestowany i poprawnie go zakodowałem. ale przekazuję sposób na osiągnięcie tego, co chcesz.

Po pierwsze tworzenie i interfejs

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");
    }
}

I zaimplementuj to do dowolnego działania.

i nazwać to tak

ProfileView pv = new ProfileView(actvitiyContext, null, temp, tempPd);
pv.setCallBack(new TaskCompleteListener
               {
                   public void onProfileClicked(String resultStringFromProfileView){}
               });
Zar E Ahmer
źródło
1
To jest poprawna odpowiedź i powinna być oznaczona jako poprawna odpowiedź. Wiem, że odpowiedź oznaczona jako poprawna faktycznie odpowiada na pytanie OP, ale nie powinna odpowiadać na takie pytanie. Faktem jest, że nie jest dobrą praktyką przekazywanie działania w ten sposób wewnątrz widoku. Dziecko nigdy nie powinno wiedzieć o swoim rodzicu, chyba że za pośrednictwem Context. Jak stwierdza Nepster, najlepszą praktyką jest przekazywanie oddzwonienia, więc za każdym razem, gdy wydarzy się coś interesującego dla rodzica, oddzwanianie zostanie uruchomione z odpowiednimi danymi.
Darwind
6

Kontekstem może być Aplikacja, Usługa, Działanie i wiele innych.

Zwykle kontekstem widoków w działaniu jest samo działanie, więc możesz pomyśleć, że możesz po prostu rzucić ten kontekst na działanie, ale tak naprawdę nie zawsze możesz to zrobić, ponieważ w tym przypadku kontekstem może być również ContextThemeWrapper.

ContextThemeWrapper jest intensywnie używany w najnowszych wersjach AppCompat i Androida (dzięki androidowi: atrybut motywu w układach), więc osobiście nigdy nie wykonałbym tego rzutowania.

Krótka odpowiedź brzmi: nie można w sposób wiarygodny odzyskać działania z kontekstu w widoku. Przekaż działanie do widoku, wywołując metodę, która przyjmuje działanie jako parametr.

BladeCoder
źródło
3

Nigdy nie używaj getApplicationContext () z widokami.

Zawsze powinien to być kontekst działania, ponieważ widok jest dołączony do działania. Ponadto możesz mieć niestandardowy zestaw motywów, a podczas korzystania z kontekstu aplikacji wszystkie motywy zostaną utracone. Przeczytaj więcej o różnych wersjach kontekstów tutaj .

Łomża
źródło
3

A w Kotlinie:

tailrec fun Context.activity(): Activity? = when {
  this is Activity -> this
  else -> (this as? ContextWrapper)?.baseContext?.activity()
}
rjrjr
źródło
0

działalności jest specjalizacja z kontekstu tak, jeśli masz Context już znasz których działalność masz zamiar używać, a może po prostu rzucić do c ; gdzie a jest działaniem, a c jest kontekstem.

Activity a = (Activity) c;
ACLima
źródło
7
Jest to niebezpieczne, ponieważ, jak wspomniano w oddzielnym komentarzu, kontekst nie zawsze może być działaniem.
4
typecast tylko wtedy, gdy (kontekstowa instancja działania) {// typecast}
Amit Yadav
0

Użyłem konwersji działania

Activity activity = (Activity) context;
Samuel Ivan
źródło
2
Istnieją różne rodzaje kontekstów. Działania i aplikacje mogą mieć konteksty. Działa to tylko wtedy, gdy kontekst dotyczy działania.
cylov
0

Ta metoda powinna być pomocna ..!

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;

    }

Mam nadzieję, że to pomoże .. Wesołego kodowania!

Taslim Oseni
źródło
Sprawdź, czy przekazany kontekst nie ma wartości null. Najprawdopodobniej jest to problem.
Taslim Oseni
0

co powiesz na niektóre wywołania zwrotne danych na żywo,

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

class ProfileActivity{

  override fun onCreateView(...){

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

}
Abhinav Atul
źródło