Why an activity object is casted to an interface in Android? - java

In the following link https://github.com/iPaulPro/Android-ItemTouchHelper-Demo/blob/master/app/src/main/java/co/paulburke/android/itemtouchhelperdemo/MainFragment.java , Activity object is casted to an inner interface named OnListItemClickListener.
mItemClickListener.onListItemClick(position);
The whole source code of the MainFragment class is as the following :
public class MainFragment extends ListFragment {
public interface OnListItemClickListener {
void onListItemClick(int position);
}
private OnListItemClickListener mItemClickListener;
public MainFragment() {
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mItemClickListener = (OnListItemClickListener) activity;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final String[] items = getResources().getStringArray(R.array.main_items);
final ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),
android.R.layout.simple_list_item_1, items);
setListAdapter(adapter);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
mItemClickListener.onListItemClick(position);
}
}
Why does activity object casted to an interface?
There is similar question at stackoverflow How is it possible to cast an Android Activity to an interface? but it is not satisfactory for me, it does not reply my questions.

To expand on #CommonsWare answer on why you have to cast it into OnListItemClickListener. The onAttach lifecycle method of the Fragment accepts any Activity - and an Activity does not have MainFragment.OnListItemClickListener implemented as default.
So the compiler does not know that, but you know that MainActivity has implemented it and with the cast you tell the compiler that MainActivity implements MainFragment.OnListItemClickListener.

This interface is not implemented in anywhere in the codes or i cannot see it
See this line:
public class MainActivity extends ActionBarActivity implements MainFragment.OnListItemClickListener {
but i cannot see any other implementation of this method..
See these lines:
#Override
public void onListItemClick(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new RecyclerListFragment();
break;
case 1:
fragment = new RecyclerGridFragment();
break;
}
getSupportFragmentManager().beginTransaction()
.replace(R.id.content, fragment)
.addToBackStack(null)
.commit();
}
Why does activity object casted to an interface?
This is a typical implementation of the contract pattern. The fragment requires that its hosting activity implement a certain interface. That way, the fragment does not care what specific activity class is the one that hosts it.

You have to make your activity implements MainFragment.OnListItemClickListener.
Besides, in your Activity, you will ned to implement the function void onListItemClick(int position).

Related

Cannot resolve method super() error in fragmentmanager Android

I am not a Java coder but trying to build an app while trying to learn Java along the way.
I am trying to add 2 fragments one is the login fragment and one is the register fragment but while using super constructor I am facing cannot resolve error.
Could anyone suggest what I am doing wrong?
I get that super is used for accessing the parent class constructor which in my case is not empty.
Following is the MainActivity code:
public class MainActivity extends FragmentActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager2 viewPager = findViewById(R.id.viewPager);
AuthenticationPagerAdapter pagerAdapter = new AuthenticationPagerAdapter(getSupportFragmentManager());
pagerAdapter.addFragment(new fragment_login());
pagerAdapter.addFragment(new fragment_register());
viewPager.setAdapter(pagerAdapter);
}
public static class AuthenticationPagerAdapter extends FragmentStateAdapter
{
private ArrayList<Fragment> fragmentList = new ArrayList<>();
public AuthenticationPagerAdapter(FragmentManager fragmentActivity)
{
super(fragmentActivity);
}
public Fragment getItem(int i)
{
return fragmentList.get(i);
}
public int getCount()
{
return fragmentList.size();
}
void addFragment(Fragment fragment) {
fragmentList.add(fragment);
}
#NonNull
#Override
public Fragment createFragment(int position)
{
return null;
}
#Override
public int getItemCount()
{
return 0;
}
}
}
Using the super() in a constructor you are trying to call the constructor of the parent class.
In this case the FragmentStateAdapter has 3 constructors:
FragmentStateAdapter(FragmentActivity fragmentActivity)
FragmentStateAdapter(Fragment fragment)
FragmentStateAdapter(FragmentManager fragmentManager, Lifecycle lifecycle)
In your case you have:
public AuthenticationPagerAdapter(FragmentManager fragmentActivity)
and super(FragmentManager); doesn't match any super constructor.
You can use something like:
public AuthenticationPagerAdapter(FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
public AuthenticationPagerAdapter(FragmentManager fragmentManager,Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
I think it's impossible using super() for fragmentActivity parameter. You should send a values to super() method from higher situated constructor. By the way I can't see a constructor for MainActivity class. From which one class you want get a values?

Android: How to use the onFragmentInteractionListener interface provided by a Fragment by default [duplicate]

I have a wizard generated app with navigation drawer in android studio 0.8.2
I have created a fragment and added it with newInstance() and I get this error:
com.domain.myapp E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.ClassCastException: com.domain.myapp.MainActivity#422fb8f0 must implement
OnFragmentInteractionListener
I can't find anywhere how to implement this OnFragmentInteractionListener ??
It cannot be found even in android sdk documentation!
MainActivity.java
import android.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.widget.DrawerLayout;
public class MainActivity extends Activity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
/**
* Fragment managing the behaviors, interactions and presentation of the navigation drawer.
*/
private NavigationDrawerFragment mNavigationDrawerFragment;
/**
* Used to store the last screen title. For use in {#link #restoreActionBar()}.
*/
private CharSequence mTitle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getFragmentManager();
switch (position) {
case 0: fragmentManager.beginTransaction()
.replace(R.id.container, PlaceholderFragment.newInstance(position + 1))
.commit(); break;
case 1: fragmentManager.beginTransaction()
.replace(R.id.container, AboutFragment.newInstance("test1", "test2"))
.commit(); break; // this crashes the app
case 2: fragmentManager.beginTransaction()
.replace(R.id.container, BrowseQuotesFragment.newInstance("test1", "test2"))
.commit(); break; // this crashes the app
}
}
public void onSectionAttached(int number) {
switch (number) {
case 1:
mTitle = getString(R.string.title_section1);
break;
case 2:
mTitle = getString(R.string.title_section2);
break;
case 3:
mTitle = getString(R.string.title_section3);
break;
}
}
public void restoreActionBar() {
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setTitle(mTitle);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!mNavigationDrawerFragment.isDrawerOpen()) {
// Only show items in the action bar relevant to this screen
// if the drawer is not showing. Otherwise, let the drawer
// decide what to show in the action bar.
getMenuInflater().inflate(R.menu.main, menu);
restoreActionBar();
return true;
}
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";
/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static PlaceholderFragment newInstance(int sectionNumber) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((MainActivity) activity).onSectionAttached(
getArguments().getInt(ARG_SECTION_NUMBER));
}
}
}
For those of you who still don't understand after reading #meda answer, here is my concise and complete explanation for this issue:
Let's say you have 2 Fragments, Fragment_A and Fragment_B which are auto-generated from the app. On the bottom part of your generated fragments, you're going to find this code:
public class Fragment_A extends Fragment {
//rest of the code is omitted
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
public void onFragmentInteraction(Uri uri);
}
}
public class Fragment_B extends Fragment {
//rest of the code is omitted
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
public void onFragmentInteraction(Uri uri);
}
}
To overcome the issue, you have to add onFragmentInteraction method into your activity, which in my case is named MainActivity2. After that, you need to implements all fragments in the MainActivity like this:
public class MainActivity2 extends ActionBarActivity
implements Fragment_A.OnFragmentInteractionListener,
Fragment_B.OnFragmentInteractionListener,
NavigationDrawerFragment.NavigationDrawerCallbacks {
//rest code is omitted
#Override
public void onFragmentInteraction(Uri uri){
//you can leave it empty
}
}
P.S.: In short, this method could be used for communicating between fragments. For those of you who want to know more about this method, please refer to this link.
Answers posted here did not help, but the following link did:
http://developer.android.com/training/basics/fragments/communicating.html
Define an Interface
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
// Container Activity must implement this interface
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
For example, the following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.onArticleSelected(position);
}
Implement the Interface
For example, the following activity implements the interface from the above example.
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Do something here to display that article
}
}
Update for API 23: 8/31/2015
Overrided method onAttach(Activity activity) is now deprecated in android.app.Fragment, code should be upgraded to onAttach(Context context)
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
#Override
public void onStart() {
super.onStart();
try {
mListener = (OnFragmentInteractionListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(getActivity().toString()
+ " must implement OnFragmentInteractionListener");
}
}
See your auto-generated Fragment created by Android Studio. When you created the new Fragment, Studio stubbed a bunch of code for you. At the bottom of the auto-generated template there is an inner interface definition called OnFragmentInteractionListener. Your Activity needs to implement this interface. This is the recommended pattern for your Fragment to notify your Activity of events so it can then take appropriate action, such as load another Fragment. See this page for details, look for the "Creating event callbacks for the Activity" section: http://developer.android.com/guide/components/fragments.html
For those of you who visit this page looking for further clarification on this error, in my case the activity making the call to the fragment needed to have 2 implements in this case, like this:
public class MyActivity extends Activity implements
MyFragment.OnFragmentInteractionListener,
NavigationDrawerFragment.NaviationDrawerCallbacks {
...// rest of the code
}
You should try removing the following code from your fragments
try {
mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
The interface/listener is a default created so that your activity and fragments can communicate easier
In addition to #user26409021 's answer, If you have added a ItemFragment, The message in the ItemFragment is;
Activities containing this fragment MUST implement the {#link OnListFragmentInteractionListener} interface.
And You should add in your activity;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, ItemFragment.OnListFragmentInteractionListener {
//the code is omitted
public void onListFragmentInteraction(DummyContent.DummyItem uri){
//you can leave it empty
}
Here the dummy item is what you have on the bottom of your ItemFragment
With me it worked delete this code:
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
Ending like this:
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
OnFragmentInteractionListener is the default implementation for handling fragment to activity communication. This can be implemented based on your needs. Suppose if you need a function in your activity to be executed during a particular action within your fragment, you may make use of this callback method. If you don't need to have this interaction between your hosting activity and fragment, you may remove this implementation.
In short you should implement the listener in your fragment hosting activity if you need the fragment-activity interaction like this
public class MainActivity extends Activity implements
YourFragment.OnFragmentInteractionListener {..}
and your fragment should have it defined like this
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
also provide definition for void onFragmentInteraction(Uri uri); in your activity
or else just remove the listener initialisation from your fragment's onAttach if you dont have any fragment-activity interaction
Instead of Activity use context.It works for me.
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mListener = (OnFragmentInteractionListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
Just an addendum:
OnFragmentInteractionListener handle communication between Activity and Fragment using an interface (OnFragmentInteractionListener) and is created by default by Android Studio, but if you dont need to communicate with your activity, you can just get ride of it.
The goal is that you can attach your fragment to multiple activities and still reuse the same communication approach (Every activity could have its own OnFragmentInteractionListener for each fragment).
But and if im sure my fragment will be attached to only one type of activity and i want to communicate with that activity?
Then, if you dont want to use OnFragmentInteractionListener because of its verbosity, you can access your activity methods using:
((MyActivityClass) getActivity()).someMethod()
Just go to your fragment Activity and remove all method.....instead on on createview method.
your fragment has only on method oncreateview that's it.
//only this method implement other method delete
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
and make sure your layout it is demo for u.
I'd like to add the destruction of the listener when the fragment is detached from the activity or destroyed.
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
and when using the new onStart() method with Context
#Override
public void onDestroy() {
super.onDestroy();
mListener = null;
}

Managing Multiple Fragments With ONE Interface

I have One Activity and Three Fragments. The application launches and Fragment 1 is visible. I click on a button. The Fragment communicates with the Activity through the following Interface and launches Fragment 2:
public OnClickedListener listener;
static interface OnClickedListener{
public void buttonClicked(View v);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
Activity a;
if (context instanceof Activity){
a=(Activity) context;
this.listener = (OnClickedListener)a;
}
}
...
playBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.buttonClicked(v);
}
});
My MainActivity implements Fragment1.OnClickedListener, receives the data and launches Fragment 2.
Now I'm in Fragment 2. I want to click on a button and launch Fragment 3. I know that I can do that by implementing YET ANOTHER interface in Fragment 2 to then communicate to the Activity and say "Hey Launch Fragment 3". So now My Activity looks like this
MainActivity implements Fragment1.OnClickedListener,Fragment2.OnClickedListener
That's all fine but let's say that I have 20 Fragments. I don't want to have 20 interfaces implemented in my Main Activity. Is there a way to create and use a single interface to communicate between each individual Fragment and the Activity. How would that be implemented? Thank you.
You can. Create only an interface (I recommend you to create it in a separate file):
interface OnClickedListener {
void buttonClicked(Fragment fragment, View v);
}
The onButtonClicked() method accepts also a Fragment instance when a Button is clicked:
playBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// You can't use just "this", pass the class instance.
listener.buttonClicked(MyFragmentClassName.this, v);
}
});
In your Activity you will have so:
public class MainActivity extends Activity implements MainActivity.OnClickedListener {
#Override
public void buttonClicked(Fragment fragment, View v) {
// Check if the Fragment instance, or tag, or the info that you use to recognize it.
}
}
Create one nested interface in Activity or in separate file instead of nested interfaces in Fragments.

Android Access Fragment Variable

What I am basicaly trying is to access a variable inside a Fragment and get rid of it in my activity.
It worked to get the variable of the activity in my fragment but not the other way around:
what I did:
// get method of MainActivity
final MainActivity activity = (MainActivity) getActivity();
is it even possible to make this "the other way around"?
(Access variable of Fragment in my Activity)?
You need to implement listeners.
You can read more about here:Communicating with Other Fragments
Here is a code example how to pass data (or null) from Activity to a Fragment:
public class FragmentA extends Fragment implements FragmentCommunicator{
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((MainActivity)getActivity()).fragmentCommunicator = this;
}
#Override
public void passDataToFragment(String str) {
//str is the string variable you pass from the Activity, it can be null...
}
}}
Next the FragmentCommunicator Class:
public interface FragmentCommunicator{
public void passDataToFragment(String str);}
And the Activity:
public class MainActivity extends FragmentActivity{
public FragmentCommunicator fragmentCommunicator;
public void someMethod(String someString) {
fragmentCommunicator.passDataToFragment(someString);
}}
When you call passDataToFragment() from the Activity it will pass the string (or any other variable) to the fragment method passDataToFragment().
You can get your fragment by id/tag from fragment manager and do whatever you want with them.
that's super easy.
if you are adding your fragments in runtime (using FragmentManager)
you are creating objects of that fragment...just keep the reference with you and you are good to call any function or access any variable of the fragment.
e.g. you fragment is MyFragment
MyFragment mf = new MyFragment();
getSupportFragmentManger().beginTrans......you know the code to add a fragment
then simply call any method...for say... change() by mf.change();
or you can do something like
MyFragment mf = (MyFragment) findFragmentById(R.id.container);
and then again mf.change();

How to return "result" from one class to a defined class?

I am working with ViewPager i.e on top of the MainActivity class and the Viewpager class extends fragment.
The problem is that, normally when we need a class to return some result then while passing the intent we use startActivityforresult(intent,int) hence it passes the result captured in the secondactivity the class from where it's been called.
But as i am working with viewpager on top of the mainactivity and i am using floating action button, when i click the button to open the second activity it returns the result to the mainactivity but not the viewpager class.
So my question is how can i pass the result taken from the secondactivity to my desired class?
Update::
MainActivity.java
this is my main class which is using the intent as well as receiving the result from the second activity class ActivityTwo
What i have done here is
startActivityForResult(intent,1);
public void onActivityresult(i,j,intent){MyFragment fragment;
fragment.onActivityReusult(i,j,intent);//Here i have passes the values
received by this class to the fragment class where i need the values but it's not working
}
You can use a Bundle.
Take a look at this topic : Bundle
bookDescFragment = new BookDescFragment();
Bundle args = new Bundle();
args.putInt(BookDescFragment.MyVariabl, VariableValue);
myFragment.setArguments(args);
The best way to do that is to create an Interface with one method. Then the fragment implements this Interface. After that, you might call this method in the onActivityResult. You just get the desired fragment in the onActivityResult and call the method from its Interface
EDIT:
Interface example:
public interface Interface_example {
public void onActivityResult();
}
Fragment example:
public class Fragment_example extends Fragment implements Interface_example {
#Override
public void onActivityResult() {
//Here you can do whatever you want
}
}
Activity example:
public class Activity_example extends FragmentActivity {
private ArrayList<Fragment> mFragmentList;
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_example);
mFragmentList = new ArrayList<Fragment>();
mFragmentList.add(new Fragment_example());
mViewPager.setAdapter(new SimpleTabPagerAdapter(
getSupportFragmentManager(), mFragmentList));
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Fragment _fragment = mFragmentList.get(mViewPager.getCurrentItem());
if(_fragment instanceof Fragment_example){
// This line will call that method previously created
((Fragment_example) _fragment).onActivityResult();
}
}
}

Categories