Communication between Fragment and Activity in Android - android

I considered this documentation and several SO questions or different tutorials for fragment to activity communication. I'm building a simple chat for my App. When I click a chat room in my InboxFragment I want to pass the name of the chat room to my ChatActivity and add the name of the chat room to the ListView inside the ChatActivity.
The Problem is that I always get the error message that the ChatActivity doesn't implement the interface OnChatRommSelected.
The error message is:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.lyl.nairad, PID: 3018
java.lang.ClassCastException: com.lyl.nairad.Activities.MainAppActivity#2600f9ae must implement OnChatRoomSelected
at com.lyl.nairad.Fragments.InboxFragment.onStart(InboxFragment.java:137)
My InboxFragment looks like this:
EDIT: In the InboxFragment are some more variables, etc. but I let them away to keep the code as short as possible.
public class InboxFragment extends Fragment {
// [BEGIN: Communication instances
OnChatRoomSelected mCallback;
// END]
private final String TAG = "InboxFragment";
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_inbox, container, false);
callingActivity = getActivity();
// Some Code...
chats.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
sendChatRoomName(String.valueOf(chats.getItemAtPosition(position)));
Intent i = new Intent(getActivity(), ChatActivity.class);
startActivity(i);
}
});
return view;
}
public void refresh(){
((TextView)getActivity().findViewById(R.id.toolbar_title)).setText("Chats");
}
#Override
public void onResume() {
super.onResume();
refresh();
}
// [BEGIN: Interface for Fragment to Activity Communication
public interface OnChatRoomSelected {
public void selectedChatRoom(String chatRoomName);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
#Override
public void onStart() {
super.onStart();
try {
mCallback = (OnChatRoomSelected) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(getActivity().toString()
+ " must implement OnChatRoomSelected");
}
}
public void sendChatRoomName(String chatRoomName) {
mCallback.selectedChatRoom(chatRoomName);
}
// END}
}
My ChatActivity looks like this:
public class ChatActivity extends Activity implements InboxFragment.OnChatRoomSelected {
Button send;
EditText msgField;
ListView newsExtending;
ArrayList<String> messages;
ArrayAdapter<String> adapter;
private final String TAG = "ChatActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
messages = new ArrayList<>();
send = (Button) findViewById(R.id.sendMsgBtn);
msgField = (EditText) findViewById(R.id.aMessage);
newsExtending = (ListView) findViewById(R.id.privateMessagesList);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, messages);
newsExtending.setAdapter(adapter);
send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String text = msgField.getText().toString();
messages.add(text);
adapter.notifyDataSetChanged();
msgField.setText("");
}
});
}
#Override
public void selectedChatRoom(String chatRoomName) {
messages.add(chatRoomName);
adapter.notifyDataSetChanged();
}
}
When I comment out
#Override
public void onStart() {
super.onStart();
/* try {
mCallback = (OnChatRoomSelected) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(getActivity().toString()
+ " must implement OnChatRoomSelected");
}*/
}
in the InboxFragment, my error looks like this:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.lyl.nairad, PID: 3334
java.lang.NullPointerException: Attempt to invoke interface method 'void com.lyl.nairad.Fragments.InboxFragment$OnChatRoomSelected.selectedChatRoom(java.lang.String)' on a null object reference
at com.lyl.nairad.Fragments.InboxFragment.sendChatRoomName(InboxFragment.java:143)
at com.lyl.nairad.Fragments.InboxFragment$2.onItemClick(InboxFragment.java:102)

Use methods onAttach(Context context) for register your callback
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (ChatActivity) context;
} catch (ClassCastException e) {
Log.e("Custom TAG", "ClassCastException", e);
}
}
and onDetach to unregister callback
#Override
public void onDetach() {
mCallback = null;
super.onDetach();
}

You should get interface method through activity context and do it inside onAttach() callback like this
#override
public void onAttach(Context context){
super.onAttach(context);
try {
mCallback = (OnChatRoomSelected) context;
} catch (ClassCastException e) {
throw new ClassCastException(getActivity().toString()
+ " must implement OnChatRoomSelected");
}
}

Related

communicating between fragments through activity

I have two fragments FGames and FGamesDetail. which display the list of Games and when clicked should populate the FGamesDetail fragment. I am using MVP pattern.
I am trying to implement MultiPane layout for tablet to have list and detail view next to each other.
I am getting a null pointer exception at 'mListener.onGameSelected(gameEntity);' in FGames. I know I have not initialised it at this place but should I be initialising it every method I go through in MVP pattern.
GamesAdapter - RecyclerView Adapter.
#OnClick(R.id.row_container)
void rowClick(){
GamesPresenter gamesPresenter = new GamesPresenterImpl();
gamesPresenter.showGameDetail(data.get(getLayoutPosition()));
Toast.makeText(context, "itemClicked " + data.get(getLayoutPosition()), Toast.LENGTH_SHORT).show();
}
GamesPresenter - Interface
public interface GamesPresenter {
void initUi();
void showGameDetail(GameEntity gameEntity);
}
GamesPresenterImpl -
public class GamesPresenterImpl implements GamesPresenter {
GamesView gamesView;
private ApiInterface apiInterface;
/**
* Collects all subscriptions to unsubscribe later
*/
#NonNull
private CompositeDisposable mCompositeDisposable = new CompositeDisposable();
public GamesPresenterImpl() {}
public GamesPresenterImpl(GamesView gamesView) {
this.gamesView = gamesView;
}
#Override
public void initUi() {
getGamesData();
}
#Override
public void showGameDetail(GameEntity gameEntity) {
//gamesView was null so initialised here
GamesView gamesView = new FGames();
gamesView.onListItemClick(gameEntity);
}
}
GamesView - interface
public interface GamesView {
/**
* Initialise the recycler view to list Games data
* #param gameEntities
*/
void initRecyclerView(List<GameEntity> gameEntities);
void showToast(String message);
void onListItemClick(GameEntity gameEntity);
}
#
FGames - has all the implementation for the Fragment
public class FGames extends Fragment implements GamesView {
#BindView(R.id.rv_games)
RecyclerView rvGames;
private GamesAdapter gamesAdapter;
private GamesPresenterImpl presenter;
OnGameSelectedListener mListener;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.games_layout, container, false);
ButterKnife.bind(this, view);
presenter = new GamesPresenterImpl(this);
return view;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mListener = (OnGameSelectedListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener");
}
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
presenter.initUi();
}
#Override
public void initRecyclerView(List<GameEntity> gameEntities) {
gamesAdapter = new GamesAdapter(getActivity(), gameEntities);
rvGames.setAdapter(gamesAdapter);
rvGames.setLayoutManager(new LinearLayoutManager(getActivity()));
}
#Override
public void showToast(String message) {
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
}
#Override
public void onListItemClick(GameEntity gameEntity) {
//Here is where the NUll pointer exception is
mListener.onGameSelected(gameEntity);
}
public interface OnGameSelectedListener{
public void onGameSelected(GameEntity gameEntity);
}
}
MainActivity - which displays the performs the game selected operation to update the UI if detail fragment is available. I followed Android documentation to do this.
public class MainActivity extends AppCompatActivity implements FGames.OnGameSelectedListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//FGames fGames = new FGames();
//getSupportFragmentManager().beginTransaction().add(R.id.games_container, fGames).commit();
}
#Override
public void onGameSelected(GameEntity gameEntity) {
FGameDetail gameDetailFrag = (FGameDetail) getSupportFragmentManager()
.findFragmentById(R.id.fragment_fGameDetail);
if (gameDetailFrag == null) {
// DisplayFragment (Fragment B) is not in the layout (handset layout),
} else {
// DisplayFragment (Fragment B) is in the layout (tablet layout),
// so tell the fragment to update
gameDetailFrag.updateContent(gameEntity);
}
}
}
ErrorLog
Process: com.example.rao.igttest, PID: 21481
java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.rao.igttest.Games.View.FGames$OnGameSelectedListener.onGameSelected(com.example.rao.igttest.Games.Entity.GameEntity)' on a null object reference
at com.example.rao.igttest.Games.View.FGames.onListItemClick(FGames.java:73)
at com.example.rao.igttest.Games.Presenter.GamesPresenterImpl.showGameDetail(GamesPresenterImpl.java:53)
at com.example.rao.igttest.Games.View.GamesAdapter$GamesViewHolder.rowClick(GamesAdapter.java:72)
at com.example.rao.igttest.Games.View.GamesAdapter$GamesViewHolder_ViewBinding$1.doClick(GamesAdapter$GamesViewHolder_ViewBinding.java:33)
at butterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

Passing a long value from one fragment through the main activity to another fragment

I have a main_activity containing two fragments. When a button is clicked on beatTimeFragment, I want beatTimeFragment to determine the msec past 1970 that the button was pressed and pass it to toneTimeFragment by way of the main activity. The toneTimeFragment will then compare the response to a standard beat and give an offSet.
Every example i have seen passes string data rather than long data. In my code, I tried passing String data then converting it to long data. The string data gets transfered (i can was it show string). But the long gives a response of 0 or it kick me out if i do not have the exception.
Is there a way to get the conversion to work or if not, how do i set up for a long data transfer rather than String by removing the
beatTimeDisplay.getText().toString()
at the end of the beatTimeFragment?
main_activity
public class MainActivity extends AppCompatActivity implements
BeatTimeFragment.BeatTimeListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//Get called by BeatTimeFragment when button is pushed
#Override
public void sendBeatData(String beatTime) {
ToneTimeFragment toneFragment = (ToneTimeFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
toneFragment.setBeatLong(beatTime);
}
}
toneTimeFragment
public class ToneTimeFragment extends Fragment {
public void setBeatLong(String beatTime) {
try {
beatTimeL = Long.parseLong(beatTime);
} catch (NumberFormatException e) {
System.out.println("NumberFormatException: " + e.getMessage());
}
// beatTimeL = Long.parseLong(beatTime);
offSet = beatTimeL - toneTime;
offSetView.setText(beatTime + " msec");
beatTimeFragment
public class BeatTimeFragment extends Fragment {
private static Button beatBtn;
private static TextView beatTimeDisplay;
int q = 0;
long beatTime = 0;
//set up sending beatTime
BeatTimeListener activityCommander;
public interface BeatTimeListener{
public void sendBeatData(String beatTime);
}
//setup to send data to top
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
activityCommander = (BeatTimeListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString());
}
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.beat_time_fragment, container, false);
beatTimeDisplay = (TextView) view.findViewById(R.id.beatTimeDisplay);
final Button beatBtn = (Button) view.findViewById(R.id.beatBtn);
beatBtn.setOnClickListener(
new View.OnClickListener() {
public void onClick(View v){
long beatTime= System.currentTimeMillis();
TextView view1 = (TextView) view.findViewById(R.id.beatTimeDisplay);
view1.setText(beatTime + " msec");
readySendBeat(v);
}
}
);
return view;
}
//calls to send data when button clicked
public void readySendBeat(View v) {
activityCommander.sendBeatData(beatTimeDisplay.getText().toString());
}
}
While debugging, i realized that i was not passing my beatTime value into the readySendBeat method in the BeatTimeFragment. When i added the object to the method then I was able to pass the data and no longer get a value of 0.
public void readySendBeat(Long beatTime){
activityCommander.sendBeatData(beatTime);
}
Try this, you dont need call to findViewById inside button listener, neither create duplicate long field:
MainActivity:
public class MainActivity extends AppCompatActivity implements
BeatTimeFragment.BeatTimeListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//Get called by BeatTimeFragment when button is pushed
#Override
public void sendBeatData(long beatTime) {
ToneTimeFragment toneFragment = (ToneTimeFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
toneFragment.setBeatLong(beatTime);
}
}
ToneTimeFragment:
public class ToneTimeFragment extends Fragment {
public void setBeatLong(long beatTime) {
try {
beatTimeL = beatTime;
} catch (NumberFormatException e) {
System.out.println("NumberFormatException: " + e.getMessage());
}
offSet = beatTimeL - toneTime;
offSetView.setText(beatTime + " msec");
BeatTimeFragment:
public class BeatTimeFragment extends Fragment {
private static Button beatBtn;
private static TextView beatTimeDisplay;
int q = 0;
long beatTime = 0;
//set up sending beatTime
BeatTimeListener activityCommander;
public interface BeatTimeListener{
public void sendBeatData(String beatTime);
}
//setup to send data to top
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
activityCommander = (BeatTimeListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString());
}
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.beat_time_fragment, container, false);
beatTimeDisplay = (TextView) view.findViewById(R.id.beatTimeDisplay);
final Button beatBtn = (Button) view.findViewById(R.id.beatBtn);
beatBtn.setOnClickListener(
new View.OnClickListener() {
public void onClick(View v){
beatTime= System.currentTimeMillis();
beatTimeDisplay.setText(beatTime + " msec");
readySendBeat(v);
}
}
);
return view;
}
//calls to send data when button clicked
public void readySendBeat() {
activityCommander.sendBeatData(beatTime);
}
}

Only the original thread that created a view hierarchy can touch its views. - Strange behavior

This is a very strange behavior and I don't know how to fix it.
I have an Activity as a Presenter (In a MVP Architecture).
When the activity starts, I attach a Fragment as a View. The fragment itself is very simple.
public class CurrentSaleFragment extends BaseFragment {
private MainMVP.SalesPresenterOps salesPresenterOps;
private SaleAdapter adapter;
private ListView lv;
#BindView(R.id.btn_sell)
FloatingActionButton btnAdd;
public static CurrentSaleFragment newInstance(){
CurrentSaleFragment fragment = new CurrentSaleFragment();
Bundle arguments = new Bundle();
arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_quick_sale );
fragment.setArguments(arguments);
return fragment;
}
#Override
protected void init() {
super.init();
lv = (ListView)view.findViewById(R.id.lv_sale);
}
#OnClick(R.id.btn_sell)
public void addToSale(View view){
mPresenter.moveToFragment(SellProductFragment.newInstance());
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
salesPresenterOps = (MainMVP.SalesPresenterOps)context;
}
#Override
public void onDetach() {
salesPresenterOps = null;
super.onDetach();
}
}
The BaseFragment from which this fragmend extends :
public class BaseFragment extends Fragment implements MainMVP.RequiredViewOps, View.OnClickListener,
LoaderRequiredOps{
protected View view;
protected MainMVP.PresenterOps mPresenter;
protected final static String LAYOUT_RES_ID = "layout_res_id";
#Override
public void showOperationResult(String message, final long rowId) {
Snackbar.make(view, message, Snackbar.LENGTH_LONG).setAction(
R.string.see, new View.OnClickListener() {
#Override
public void onClick(View v) {
onOperationResultClick(rowId);
}
}
).show();
}
#Override
public void showSnackBar(String msg) {
Snackbar.make(view, msg, Snackbar.LENGTH_SHORT).show();
}
#Override
public void showAlert(String msg) {}
protected void onOperationResultClick(long rowId){}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mPresenter = (MainMVP.PresenterOps)context;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
this.view = inflater.inflate(getArguments().getInt(LAYOUT_RES_ID), null);
init();
return view;
}
protected void addToClickListener(View ... params){
for (View v : params){
v.setOnClickListener(this);
}
}
protected void init() {
if (view != null){
ButterKnife.bind(this, view);
}
}
#Override
public void onDetach() {
mPresenter = null;
Log.d(getClass().getSimpleName(), "Fragment was detached");
super.onDetach();
}
#Override
public void onClick(View v) {}
#Override
public void onPreLoad() {
Dialogs.buildLoadingDialog(getContext(), "Loading...").show();
}
#Override
public void onLoad() {}
#Override
public void onDoneLoading() {
Dialogs.dismiss();
}
}
When I enter the method 'moveToFragment()' I just replace CurrentSaleFragment for a new Fragment:
protected void addFragment(BaseFragment fragment){
mView = fragment;
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_holder,
fragment, null).addToBackStack(null).commit();
}
Then the new fragment is attached:
public class SellProductFragment extends BaseFragment{
private ListView listView;
private ProductListAdapter adapter;
private MainMVP.SalesPresenterOps mSalesPresenter;
public static SellProductFragment newInstance(){
SellProductFragment fragment = new SellProductFragment();
Bundle arguments = new Bundle();
arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_inventory);
fragment.setArguments(arguments);
return fragment;
}
private void reload(){
final Loader loader = new Loader(this);
loader.execute();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mSalesPresenter = (MainMVP.SalesPresenterOps)context;
}
#Override
protected void init() {
super.init();
listView = (ListView)view.findViewById(R.id.lv_inventory);
reload();
FloatingActionButton button = (FloatingActionButton)view.findViewById(R.id.btn_add);
addToClickListener(button);
}
#Override
public void onLoad() {
adapter = new ProductListAdapter(getActivity().getApplicationContext(), R.layout.row_product_item,
mSalesPresenter.getProducts());
try{
updateListView();
}catch (Exception e){
Log.w(getClass().getSimpleName(), e.getMessage());
}
}
private void updateListView(){
if (adapter != null && listView != null){
listView.setAdapter(adapter);
}else{
throw new RuntimeException();
}
}
}
See that This fragment also extends from BaseFragment and implements LoaderRequiredOps. The interface is used to 'load' any data. It adds a dialog and updated the adapter when the loading is done:
public class Loader extends AsyncTask<Void, Void, Void> {
private LoaderRequiredOps presenter;
public Loader(LoaderRequiredOps presenter){
this.presenter = presenter;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
presenter.onPreLoad();
}
#Override
protected Void doInBackground(Void... params) {
presenter.onLoad();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
presenter.onDoneLoading();
presenter = null;
}
}
Now, when I try to execute the method reload() from the SellProductFragment i get the 'Only the original thread that created a view hierarchy can touch its views.'
This does not happen if the SellProductFragment is attached first instead of CurrentSaleFragment.
What is happening here?
Your Async Loader class calls the presenters method onLoad() from a background thread during doInBackground().
My guess is that in the onLoad() method of the presenter, a view is referenced.
In order to change the view at this point, post the view logic as a Runnable to the UI thread (you said your presenter is the activity, so this should be possible from the onLoad method).
#Override
public void onLoad() {
runOnUiThread(new Runnable() {
#Override
public void run() {
// Your ui code here...
}
});
// Rest of your code here...
}
For an unknown reason, an unidentified configuration allows to execute the setting of an adapter for a ListView on the doInBackground() method.
Moved it to onPostExecute() and now it's working

OnClickListener called after fragment detach

I've some rare crash reports of a live app, with OnClickListener of a Gridview being called after the destruction of its parent fragment.
Here's a MCVE of the situation:
public class MyFragment extends Fragment
{
private Activity activity;
#Override
public void onAttach(Activity activity) {
super.onAttach();
this.activity = activity;
}
#Override
public void onDetach() {
super.onDetach();
this.activity = null;
}
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
activity.doSomething();
}
}
}
}
activity.doSomething() sometimes crashes with a NullPointerException. There's no other place where I write on this.activity besides onAtach() and onDetach().
I understand that a quick fix would be just checking if the Fragment isAdded() or the activity field for null, but I want to find the reason behind this crash.
Is this a bug, or expected behaviour and I should always be checking the state of the fragment?
As #M D and #shkschneider mentioned above, you should use Interface here
I have tried to provide sample here with code :
public class MyFragment extends Fragment
{
private Activity activity;
private ArrayList<DoSomethingInterface> callback = new ArrayList<DoSomethingInterface>();
private DoSomethingInterface callback1;
public MyFragment(DoSomethingInterface interface) {
this.callback1 = interface;
}
#Override
public void onAttach(Activity activity) {
super.onAttach();
this.activity = activity;
}
#Override
public void onDetach() {
super.onDetach();
this.activity = null;
}
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
//activity.doSomething();
if(callback.size() > 0) {
for(int i = 0; i < callback.size(); i++) {
callback.get(i).doSomething();
}
}
if(callback1 != null) {
callback1.doSomething();
}
}
}
}
public interface DoSomethingInterface {
public void doSomething();
}
public void registerListener(DoSomethingInterface interface) {
//this.callback = interface;
callback.add(interface);
}
}
OtherActivity.java :
public OtherActivity extends Activity implements doSomethingInterface {
private MyFragment myFragmentObject;
public void onCreate() {
// pass reference to MyFragment using constructor
myFragmentObject = new MyFragment(this);
// or other way, using a method
myFragmentObject.registerListener(this);
}
#Override
public void doSomething() {
// callback will come
}
}
Basically you need to remove the listener in onDetach(). The reason is the Fragment may be amid of detaching, and a UI listener may be triggered after detached. That will confuse most apps.
Code suggestion:
#Override
public void onDetach() {
if (gridView != null) {
gridView.setOnItemClickListener( null );
}
super.onDetach();
// this.activity = null;
}
Note:
Call setOnItemClickListener() passing null as parameter. I did not find any documentation to say this but this is a known trick. Besides that, there are some listeners with custom removal methods.
I would remove code this.activity = null because this may be the code that caused the crash/exception. And conceptually it's better that the Activity class set this object instead of the Fragment class.

Call method of interface implements with child fragment From container activity Android

I'm stuck with communication between activity and fragment using interface. I have created activity with child fragment. I wanna do some stuff with continuous thread defined in activity and during that thread when I'm getting some result at that time I wanna trigger to child fragment to do something.
My Container Activity
public class MySpaceActivity extends BaseDrawerActivity {
private OnSetLastSeenListener mListner;
public static Thread mThread = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setHeaders(Const.MY_SPACE);
super.setSubmenus(Const.MY_SPACE,
Utils.getSubmenuList(Const.MY_SPACE, MySpaceActivity.this),
submenuBean);
// super.attachFragment(submenuBean);
}
#Override
public void setHeaderSubMenu(SubmenuBean subMenuBean) {
// txt_submenu.setText(subMenuBean.getSubmenu_name());
this.submenuBean = subMenuBean;
Log.print("::::: setHeaderSubMenu ::::");
super.attachFragment(submenuBean);
}
public void setsubFragment(SubmenuBean subMenuBean) {
this.submenuBean = subMenuBean;
super.attachSubFragment(submenuBean);
}
#Override
public void onBackPressed() {
super.onBackPressed();
popLastFragment();
}
private void popLastFragment() {
if (super.getNumberOfChilds() > 1) {
super.popSubFragment();
} else {
finish();
}
}
#Override
protected Fragment getFragement() {
StudentsFragment fragment = new StudentsFragment(Const.MY_SPACE,
getSubmenubean());
return fragment;
}
public SubmenuBean getSubmenubean() {
return submenuBean;
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
mThread = new Thread(new CountDownTimer(MySpaceActivity.this));
mThread.start();
}
#Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
if (mThread.isAlive()) {
mThread.interrupt();
mThread = null;
}
}
public void updateLastSeen(){
Log.print("::::::Call Interface::::::");
mListner.updateLastSeen();
}
class CountDownTimer implements Runnable {
private Context mContext;
private JSONObject mJsonObject;
private JSONArray mJsonArray;
public CountDownTimer(Context mContext) {
this.mContext = mContext;
}
// #Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
HttpChatLastSeen mChat = new HttpChatLastSeen();
mJsonObject = mChat.Http_ChatLastSeen(mContext);
String mResult = mJsonObject.getString("Result");
if (mResult.equalsIgnoreCase(String
.valueOf(Const.RESULT_OK))) {
mJsonArray = mJsonObject.getJSONArray("UserData");
for (int i = 0; i < mJsonArray.length(); i++) {
mJsonObject = mJsonArray.getJSONObject(i);
new DbStudentMasterBll(mContext).update(
"last_seen", mJsonObject
.getString("LastSeen"), Integer
.parseInt(mJsonObject
.getString("UserId")));
}
} else {
Log.print("MY LAST SEEN Response : "
+ mJsonObject.toString());
}
updateLastSeen();
Thread.sleep(15000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
Log.print("ChatLastSeenThread : ", e.getMessage());
}
}
}
}
}
My Child Fragment With Interface :
public class StudentsFragment extends Fragment implements OnSetLastSeenListener{
TextView txt_submenu;
ListView list_students;
SubmenuBean submenuBean;
int Mainmenu;
MySpaceActivity mMySpaceActivity;
ArrayList<DbStudentMasterBean> studentsList;
StudentsAdapter mAdapter = null;
OnSetLastSeenListener mListner;
public StudentsFragment() {
super();
}
public StudentsFragment(int Mainmenu, SubmenuBean submenuBean) {
this.submenuBean = submenuBean;
this.Mainmenu = Mainmenu;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_students, container,
false);
mMySpaceActivity = (MySpaceActivity) getActivity();
txt_submenu = (TextView) view.findViewById(R.id.txt_submenu);
txt_submenu.setText(submenuBean.getSubmenu_name());
txt_submenu.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mMySpaceActivity.openDrawer();
}
});
list_students = (ListView) view.findViewById(R.id.list_colleagues);
studentsList = new DbStudentMasterBll(getActivity()).getAllRecords();
mAdapter = new StudentsAdapter(getActivity(), studentsList, handler);
list_students.setAdapter(mAdapter);
list_students.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
DbStudentMasterBean bean = (DbStudentMasterBean) parent
.getAdapter().getItem(position);
Message msg = new Message();
msg.what = CHAT;
msg.obj = bean;
handler.sendMessage(msg);
}
});
return view;
}
Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case CHAT:
submenuBean.setTag(VIEWCHATSTUDENT);
DbStudentMasterBean bean = (DbStudentMasterBean) msg.obj;
mMySpaceActivity.setsubFragment(submenuBean);
break;
}
};
};
#Override
public void updateLastSeen() {
// TODO Auto-generated method stub
Log.print("!!!!!!!!!Refresh Adapter!!!!!!!!!!!");
mAdapter.notifyDataSetChanged();
}
}
My Interface :
public interface OnSetLastSeenListener {
public void updateLastSeen();
}
So I have implemented interface OnSetLastSeenListener with my child fragment StudentsFragment . Now I'm calling method of tht interface updateLastSeen() from my container activity with thread. But it is not getting trigger to child fragment where I have implemented interface. So I don't know whether it is good way to communicate or not? Let me take your help to suggest on this solution or best way to communicate from child fragment to parent activity.
Thanks,
It is better to use interface when you want to communicate something from Fragment to Activity and not vice versa.
In your case, you can directly call the method in Fragment from Activity through fragment object. No need to use interface.
Something like this (For static fragments)
StudentsFragment fragment = (StudentsFragment) getFragmentManager()
.findFragmentById(R.id.fragmentid);
if (fragment != null && fragment.isInLayout()) {
fragment.updateLastSeen();
}
For dynamic fragment you can use the fragment object directly.

Categories

Resources