Two activities using the same fragment - problem - android

Two activities using the same fragment. The fragment has a text view.
Main activity writes “message 1” into the text view and it shows up.
A button in the main activity launches the second activity “for result”.
The Second activity writes “message 2” into the text view and it shows up.
A button in the second activity does set Result Activity.RESULT_OK and then finish().
The main activity gets the “onActivityResult” Result OK and writes “message 3” into the text view.
However “Message 3” does not show up in the text view. Instead “message 1” shows up.
public class MainActivity extends AppCompatActivity {
private static Context context;
private static Button btn_main;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
btn_main = findViewById(R.id.btn_main);
FragmentDisplay.setMessage1("Message 1");
btn_main.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
secondactivityLauncher.launch(intent);
}
});
}
ActivityResultLauncher<Intent> secondactivityLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
FragmentDisplay.setMessage1("Message 3");
}
}
});
public static Context getContext(){
return context;
}
}
public class SecondActivity extends AppCompatActivity {
private static Button btn_second;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
btn_second = findViewById(R.id.btn_second);
FragmentDisplay.setMessage1("Message 2");
btn_second.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = getIntent();
setResult(Activity.RESULT_OK, intent);
finish();
}
});
}
}
public class FragmentDisplay extends androidx.fragment.app.Fragment {
private static TextView textView1;
public FragmentDisplay() {
// Required empty public constructor
}
RecyclerView mRecyclerView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.display_fragment, null);
textView1 = (TextView)view.findViewById(R.id.tv1);
return view;
}
public static void setMessage1(String str){
textView1.setText(str);
}
} // end of class
//activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main.MainActivity">
<fragment
android:id="#+id/display_fragment"
android:name="ddi.pos.display.FragmentDisplay"
android:layout_width="700dp"
android:layout_height="180dp"
android:background="#00CC00"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginTop="80dp" />
<Button
android:id="#+id/btn_main"
android:layout_below="#+id/display_fragment"
android:layout_marginTop="100dp"
android:layout_marginLeft="50dp"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#FFFFFF00"
android:textSize="25sp"
android:text="Start Second Activity"
/>
//second_activity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main.MainActivity">
<fragment
android:id="#+id/display_fragment"
android:name="ddi.pos.display.FragmentDisplay"
android:layout_width="700dp"
android:layout_height="180dp"
android:background="#00CC00"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginTop="80dp" />
<Button
android:id="#+id/btn_second"
android:layout_below="#+id/display_fragment"
android:layout_marginTop="100dp"
android:layout_marginLeft="300dp"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#000000"
android:textSize="25sp"
android:text="Finish Second Activity"
/>
//display_fragment.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CC5500"
>
<TextView android:id="#+id/tv1"
android:background="#0055FF"
android:layout_height="60dp"
android:layout_width="600dp"
android:text=""
android:layout_marginLeft="30dp"
android:layout_marginTop="30dp"
android:textSize="20dp"
android:textColor="#ff000000"
/>

Caveat: I suspect that what you posted is not what you actually want to do, but a workaround of some kind so this answer may or may not actually address your use-case. It does, however, produce the behavior you asked for in the question. You said you are not trying to send data between activities, but you want the message in the first activity to change in response to actions in the second activity which implies information may be shared.
Main Answer:
The example below, using a shared ViewModel between Activity and Fragment and using data transfer across activities using intents has the behavior you describe in your question.
The ViewModel allows sharing of data between the Activity and Fragment, since the Fragment can observe the LiveData and respond when the activity changes it. Since the question calls startActivityForResult and handles the result, I used those to handle passing data back to change the message.
MainActivity.java
public class MainActivity extends AppCompatActivity {
ActivityResultLauncher<Intent> secondActivityLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
// as you indicated:
//viewModel.setMessage("Message 3");
// or like this if you sent data
Intent data = result.getData();
if( data != null ) {
Bundle extras = data.getExtras();
if( extras != null ) {
String msg = extras.getString("response");
viewModel.setMessage(msg);
}
}
}
}
});
private MainViewModel viewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewModel = new ViewModelProvider(this).get(MainViewModel.class);
// Always initialize the message to "Message 1"
viewModel.setMessage("Message 1");
Button btn = findViewById(R.id.btn_main);
btn.setOnClickListener(view -> {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("message", "Message 2");
secondActivityLauncher.launch(intent);
});
}
}
SecondActivity.java
public class SecondActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
// This ViewModel instance is "not" the same instance as the one from MainActivity, it is
// just to facilitate communication between the Activity and Fragment
MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class);
// as you had it with hard-coded message 2
// viewModel.setMessage("Message 2");
// or like this if you sent the message
Intent i = getIntent();
Bundle b = i.getExtras();
if( b != null ) {
String msg = b.getString("message");
viewModel.setMessage(msg);
}
Button btn = findViewById(R.id.btn_second);
btn.setOnClickListener(view -> {
Intent intent = new Intent();
intent.putExtra("response", "Message 3");
setResult(Activity.RESULT_OK, intent);
finish();
});
}
}
MainViewModel.java
public class MainViewModel extends ViewModel {
private final MutableLiveData<String> message_to_display = new MutableLiveData<>();
LiveData<String> message() { return message_to_display; }
void setMessage(String msg) {
message_to_display.postValue(msg);
}
}
DisplayFragment.java
public class DisplayFragment extends Fragment {
public DisplayFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_display, container, false);
}
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView txt = view.findViewById(R.id.tv1);
// Get the ViewModel from the hosting activity, could be
// Main or Second, and observe its message. Update the
// TextView if the message is changed.
MainViewModel viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
viewModel.message().observe(getViewLifecycleOwner(), s -> {
txt.setText(s);
});
}
}

Related

Android check box tik mark disappear randomly when moving from one fragment to another using android-material-stepper

Instead of getting checkbox like this(i am getting it only few times randomly when moving from on fragment to another).
I am getting like this(checkbox is still true but only tik mark is missing randomly)
I am using onSaveInstanceState and onViewStateRestored. The problem is the checkbox tik mark only disappears and comes back few times but the state of checkbox is still selected i see blue color around all the selected check boxes that selected color doesn't go away only the tik mark goes away and comes back randomly.
Layout:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="#+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:drawableLeft="#drawable/ic_tv"
android:text="TV"
android:theme="#style/CheckBoxTheme"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Tv" />
</android.support.constraint.ConstraintLayout>
</ScrollView>
My Fragment:
public class StepFragmentTwo extends Fragment implements BlockingStep {
private static final String CLICKS_KEY = "clicks";
private static final String TAG = "ADERVERTISMENT";
private int i = 0;
FragmentManager fm = getFragmentManager();
CheckBox c;
Boolean tv = false ;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(getLayoutResId(), container, false);
c = (CheckBox)v.findViewById(R.id.checkBox);
//initialize your UI
return v;
}
protected int getLayoutResId() {
return getArguments().getInt(String.valueOf(R.layout.step2));
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(CLICKS_KEY, i);
super.onSaveInstanceState(outState);
if(outState!=null) {
outState.putBoolean("c", c.isChecked());
}
}
#Override
public void onViewStateRestored(#Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
if(savedInstanceState!=null) {
tv = savedInstanceState.getBoolean("c");
}
}
#Override
public void onResume() {
super.onResume();
c.setChecked(tv);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
public void onNextClicked(final StepperLayout.OnNextClickedCallback callback) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (c.isChecked()) {
tv = true;
}
SharedPreferences shared = getActivity().getSharedPreferences("Mypref", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = shared.edit();
editor.putBoolean("tv", tv);
editor.apply(); // This line is IMPORTANT !!!
callback.goToNextStep();
}
}, 200L);
}
#Override
#UiThread
public void onCompleteClicked(final StepperLayout.OnCompleteClickedCallback callback) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
callback.complete();
}
}, 2000L);
}
public static StepFragmentTwo newInstance(#LayoutRes int layoutResId) {
Bundle args = new Bundle();
args.putInt(String.valueOf(R.layout.step2), layoutResId);
StepFragmentTwo fragment = new StepFragmentTwo();
fragment.setArguments(args);
return fragment;
}
#Override
public VerificationError verifyStep() {
//return null if the user can go to the next step, create a new VerificationError instance otherwise
return null;
}
#Override
public void onSelected() {
//update UI when selected
}
#Override
public void onError(#NonNull VerificationError error) {
//handle error inside of the fragment, e.g. show error on EditText
}
public void onBackClicked(StepperLayout.OnBackClickedCallback callback) {
//Toast.makeText(this.getContext(), "Your custom back action. Here you should cancel currently running operations", Toast.LENGTH_SHORT).show();
callback.goToPrevStep();
}
}
In short you can do,
#Override
public void onResume() {
super.onResume();
SharedPreferences shared = getActivity().getSharedPreferences("Mypref", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = shared.edit();
tv = editor.getBoolean("tv", tv);
c.setChecked(tv);
}

I've failed to show lists of a RecyclerView

I am building my first android app using WiFi Direct to transfer
text, image, and media files to peers.I am using a RecyclerView
and a Fragment. I hope to show peers's list on a framelayout
(a simple textview) and then click the views to form a group
to send files. I am stuck at the first stage of making the list shown
on the parent's view. I would appreciate your help on what I did wrong.
I am just a beginner. Any advice or resources would be welcome even besides the pending problem. The followings are three classes and two xmls I've made fumbling around many sources and Android Developer's guide.
public class MainActivity extends FragmentActivity {
public static String TAG="test";
IntentFilter mIntentFilter;
WifiP2pManager.Channel mChannel;
WifiP2pManager mManager;
BroadcastReceiver mReceiver;
public static boolean setIsWifiP2pEnable;
Button open, connect, file, image, media;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
open = (Button) findViewById(R.id.wifi_open);
connect = (Button) findViewById(R.id.connect);
file = (Button) findViewById(R.id.file);
image = (Button) findViewById(R.id.gallery);
media = (Button) findViewById(R.id.media);
open.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
startActivity(intent);
}
});
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mManager.initialize(this, getMainLooper(), null);
mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
checkWiFi();
connect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Log.d (TAG, "search start");
}
#Override
public void onFailure(int reasonCode) {
Log.d (TAG, "search failed");
}
});
}
});
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if (fragment == null) {
fragment = new DeviceFragment();
fm.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
Log.d (TAG, "fragment_activated");
}
}
#Override
protected void onResume () {
super.onResume();
mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
registerReceiver(mReceiver, mIntentFilter);
}
#Override
protected void onPause () {
super.onPause();
unregisterReceiver(mReceiver);
}
public void checkWiFi(){
if(setIsWifiP2pEnable==true){
Toast.makeText(MainActivity.this, "WiFi Direct connected", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "Open WiFi Direct in settings", Toast.LENGTH_LONG).show();
}
}
}
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {
public static WifiP2pManager mManager;
public static WifiP2pManager.Channel mChannel;
private MainActivity mActivity;
public static List<WifiP2pDevice> peers = new ArrayList<>();
public WiFiDirectBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel,
MainActivity activity) {
super();
this.mManager = manager;
this.mChannel = channel;
this.mActivity = activity;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
int state = intent.getIntExtra(mManager.EXTRA_WIFI_STATE, -1);
if (state==mManager.WIFI_P2P_STATE_ENABLED){
Log.d (MainActivity.TAG, "WiFi_enabled");
mActivity.setIsWifiP2pEnable=true;
mActivity.checkWiFi();
} else {
Log.d (MainActivity.TAG, "WiFi_failed");
mActivity.setIsWifiP2pEnable=false;
mActivity.checkWiFi();
}
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
if (mManager != null) mManager.requestPeers(mChannel, new WifiP2pManager.PeerListListener() {
#Override
public void onPeersAvailable(WifiP2pDeviceList peerList) {
Log.d (MainActivity.TAG, "peers_found");
peers.clear();
peers.addAll(peerList.getDeviceList());
if(peers.size() ==0){
Log.d (MainActivity.TAG, "No_Peers_found");
return;
}
}
});
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
// Respond to new connection or disconnections
if (mManager==null) {
return;
}
NetworkInfo networkInfo = (NetworkInfo) intent
.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()){
mManager.requestConnectionInfo(mChannel, new WifiP2pManager.ConnectionInfoListener() {
#Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
InetAddress groupOwnerAddress = info.groupOwnerAddress;
String s=groupOwnerAddress.getHostAddress();
if (info.groupFormed && info.isGroupOwner) {
} else if (info.groupFormed) {
}
}
});
}
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
}
}
}
public class DeviceFragment extends Fragment {
private RecyclerView mDeviceRecyclerView;
private DeviceAdapter mAdapter;
private List<WifiP2pDevice> mDevices;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_device_list, container,false);
mDeviceRecyclerView = (RecyclerView) view
.findViewById(R.id.device_recycler_view);
mDeviceRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
updateUI();
return view;
}
private void updateUI(){
mDevices = WiFiDirectBroadcastReceiver.peers;
mAdapter = new DeviceAdapter(mDevices);
mDeviceRecyclerView.setAdapter(mAdapter);
Log.d (MainActivity.TAG, "adapter_connected");
}
private class DeviceHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView mTitleTextView;
public DeviceHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
mTitleTextView = (TextView) itemView;
Log.d(MainActivity.TAG, "Item_Click_try");
}
#Override
public void onClick(View v) {
Toast.makeText(getActivity(),"Click Succeeded", Toast.LENGTH_LONG).show();
int i = (int) v.getTag();
WifiP2pDevice device = mDevices.get(i);
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
WiFiDirectBroadcastReceiver.mManager.connect(WiFiDirectBroadcastReceiver.mChannel, config, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Log.d (MainActivity.TAG, "Click_Success!!");
}
#Override
public void onFailure(int reason) {
Log.d (MainActivity.TAG, "Click_Failed!!");
}
});
}
}
private class DeviceAdapter extends RecyclerView.Adapter<DeviceHolder>{
private List<WifiP2pDevice> Devices;
public DeviceAdapter(List<WifiP2pDevice> devices){
Devices=devices;
Log.d (MainActivity.TAG, "device_list");
}
#Override
public DeviceHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
View view=layoutInflater
.inflate(android.R.layout.simple_list_item_1, parent, false);
Log.d (MainActivity.TAG, "simple_list");
return new DeviceHolder(view);
}
#Override
public void onBindViewHolder(DeviceHolder holder, int position) {
WifiP2pDevice device = Devices.get(position);
holder.mTitleTextView.setTag(position);
holder.mTitleTextView.setText(device.deviceName);
Log.d (MainActivity.TAG, "Device_bound");
}
#Override
public int getItemCount() {
return mDevices.size();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content"
tools:context="com.moon.android.wifidirectproject_moon.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/wifi_open"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="WiFi_Direct" />
<Button
android:id="#+id/connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Connect" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="file" />
<Button
android:id="#+id/gallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="gallery" />
<Button
android:id="#+id/media"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="media" />
</LinearLayout>
The Other Layout:
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/device_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
I think I have made some (not complete) progress on my question. I suspected that the ArrayList information is not properly delivered to my fragment class. So I wrote the following code in OnCreate method in MainActivity:
DeviceFragment df = new DeviceFragment();
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("bundle",WiFiDirectBroadcastReceiver.peers);
df.setArguments(bundle);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if (fragment == null) {
fragment = df;
fm.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
Log.d (TAG, "fragment_activated");
}
I put the following code in my fragment class:
mDevices = getArguments().getParcelableArrayList("bundle");
and revised Adapter class as follows:
public DeviceAdapter(List<WifiP2pDevice> devices){
Devices=devices;
notifyDataSetChanged();
Log.d (MainActivity.TAG, "device_list");
}
It worked. But I met with one more problem. The textviews on screen are refreshed only when I turned off and on again (not powered on/off). So, I want to make a "refresh button" in MainActivity so that I can refresh the list like when turning on/off. But I have no idea how I can refresh the RecyclerView's fragment in MainActivity. I think I should work on fragmentManager and transaction. Could anybody help me deal with this issue?

passing data from (grand) parent to parent to child to (grand) child and back up to (Grand) parent activity in android

I have a grand parent activity called Department
public class Department extends AppCompatActivity {
RecyclerView recyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_department);
.........
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, DeptDetail.class);
Bundle extra = new Bundle();
extra.putString("Department", getAdapterPosition()+"");
intent.putExtras(extra);
context.startActivity(intent);
}
});
}
}
Sends data about Department position to DepartmentDeatail activity
public class DeptDetail extends AppCompatActivity implements View.OnClickListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dept_detail);
Bundle extra = getIntent().getExtras();
deptpos = Integer.parseInt(extra.getString("Department"));
.........
public void onClick(View v) {
int id= v.getId();
Intent in;
Bundle extras = new Bundle();
in = new Intent(DeptDetail.this, Mission.class);
extras.putString("Mission",mission[deptpos]);
extras.putString("Deptid", deptpos+"");
in.putExtras(extras);
startActivityForResult(in,1);
}
}
and DeptDetail activity sends same Deptpos to its child activity Mission
public class Mission extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mission);
Bundle extra = getIntent().getExtras();
String mission = extra.getString("Mission");
deptid=Integer.parseInt(extra.getString("Deptid"));
TextView txtmission = (TextView)findViewById(R.id.txtmission);
try {
txtmission.setText(mission);
}
catch (NullPointerException e)
{
txtmission.setText("");
}
}
}
And now I want same Deptid to be accessed in DeptDetail activity which always calls for intent from Department activity, which is not available as usual..
So please show me the way to pass the data to child and back to parent.
I tried
onActivityResult(..)
but it wasn't called before onCreate where extra is read and generating NullPointerException

This android code looks fine but is not running on the emulator

I created this just to understand what intents do and how they do it:
first activity:
public class FirstActivity extends AppCompatActivity {
private Button launch;
private EditText label;
private String msg;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
launch=(Button)findViewById(R.id.launch);
label=(EditText)findViewById(R.id.et);
msg= label.getText().toString();
launch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent=SecondActivity.newIntent(FirstActivity.this, msg);
startActivity(intent);
}
});
}
}
second activity:
public class SecondActivity extends AppCompatActivity {
private EditText et1;
private static String EXTRA ="extra";
private static String message="msg";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
et1=(EditText)findViewById(R.id.et1);
message=getIntent().getStringExtra(message);
et1.setText(message);
}
public static Intent newIntent(Context packageContext, String msg) {
Intent i = new Intent(packageContext, SecondActivity.class);
i.putExtra(EXTRA, msg);
return i;
}
}
and these are the xml files of the two activities(although I dont think these will be needed but if necessary):
first activity:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<EditText
android:text="First Activity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/et"
android:inputType="text"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Launch the second activity"
android:id="#+id/launch"/>
</LinearLayout>
second activity:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Second Activity"
android:id="#+id/et1"/>
</LinearLayout>
You should do it the regular way, don't create any method.
In your first activity :
#Override
public void onClick(View v) {
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
And it should work.
You can add content to your intent (like you tried with your msg) this way :
intent.putExtra("Value1", "This value one for ActivityTwo ");
intent.putExtra("Value2", "This value two ActivityTwo");
Then in activity 2 you retrieve the datas like this :
Bundle extras = getIntent().getExtras();
if (extras == null) {
return;
}
// get data via the key
String value1 = extras.getString(Intent.EXTRA_TEXT);
if (value1 != null) {
// do something with the data
}
#Override
public void onClick(View v) {
Intent intent=SecondActivity.newIntent(FirstActivity.this, targetActivity.class);
startActivity(intent);
}
public class FirstActivity extends AppCompatActivity {
private Button launch;
private EditText label;
private String msg;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
launch=(Button)findViewById(R.id.launch);
label=(EditText)findViewById(R.id.et);
launch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
msg= label.getText().toString();
Intent intent=SecondActivity.newIntent(FirstActivity.this, SecondActivity.class);
intent.putExtra("msg",msg);
startActivity(intent);
}
});
}
}

Basic communication between two fragments

I have one activity - MainActivity. Within this activity I have two fragments, both of which I created declaratively within the xml.
I am trying to pass the String of text input by the user into Fragment A to the text view in Fragment B. However, this is proving to be very difficult. Does anyone know how I might achieve this?
I am aware that a fragment can get a reference to it's activity using getActivity(). So I'm guessing I would start there?
Have a look at the Android developers page:
http://developer.android.com/training/basics/fragments/communicating.html#DefineInterface
Basically, you define an interface in your Fragment A, and let your Activity implement that Interface. Now you can call the interface method in your Fragment, and your Activity will receive the event. Now in your activity, you can call your second Fragment to update the textview with the received value
Your Activity implements your interface (See FragmentA below)
public class YourActivity implements FragmentA.TextClicked{
#Override
public void sendText(String text){
// Get Fragment B
FraB frag = (FragB)
getSupportFragmentManager().findFragmentById(R.id.fragment_b);
frag.updateText(text);
}
}
Fragment A defines an Interface, and calls the method when needed
public class FragA extends Fragment{
TextClicked mCallback;
public interface TextClicked{
public void sendText(String text);
}
#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 = (TextClicked) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}
public void someMethod(){
mCallback.sendText("YOUR TEXT");
}
#Override
public void onDetach() {
mCallback = null; // => avoid leaking, thanks #Deepscorn
super.onDetach();
}
}
Fragment B has a public method to do something with the text
public class FragB extends Fragment{
public void updateText(String text){
// Here you have it
}
}
Some of the other examples (and even the documentation at the time of this writing) use outdated onAttach methods. Here is a full updated example.
Notes
You don't want the Fragments talking directly to each other or to the Activity. That ties them to a particular Activity and makes reuse difficult.
The solution is to make an callback listener interface that the Activity will implement. When the Fragment wants to send a message to another Fragment or its parent activity, it can do it through the interface.
It is ok for the Activity to communicate directly to its child fragment public methods.
Thus the Activity serves as the controller, passing messages from one fragment to another.
Code
MainActivity.java
public class MainActivity extends AppCompatActivity implements GreenFragment.OnGreenFragmentListener {
private static final String BLUE_TAG = "blue";
private static final String GREEN_TAG = "green";
BlueFragment mBlueFragment;
GreenFragment mGreenFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// add fragments
FragmentManager fragmentManager = getSupportFragmentManager();
mBlueFragment = (BlueFragment) fragmentManager.findFragmentByTag(BLUE_TAG);
if (mBlueFragment == null) {
mBlueFragment = new BlueFragment();
fragmentManager.beginTransaction().add(R.id.blue_fragment_container, mBlueFragment, BLUE_TAG).commit();
}
mGreenFragment = (GreenFragment) fragmentManager.findFragmentByTag(GREEN_TAG);
if (mGreenFragment == null) {
mGreenFragment = new GreenFragment();
fragmentManager.beginTransaction().add(R.id.green_fragment_container, mGreenFragment, GREEN_TAG).commit();
}
}
// The Activity handles receiving a message from one Fragment
// and passing it on to the other Fragment
#Override
public void messageFromGreenFragment(String message) {
mBlueFragment.youveGotMail(message);
}
}
GreenFragment.java
public class GreenFragment extends Fragment {
private OnGreenFragmentListener mCallback;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_green, container, false);
Button button = v.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String message = "Hello, Blue! I'm Green.";
mCallback.messageFromGreenFragment(message);
}
});
return v;
}
// This is the interface that the Activity will implement
// so that this Fragment can communicate with the Activity.
public interface OnGreenFragmentListener {
void messageFromGreenFragment(String text);
}
// This method insures that the Activity has actually implemented our
// listener and that it isn't null.
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnGreenFragmentListener) {
mCallback = (OnGreenFragmentListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnGreenFragmentListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mCallback = null;
}
}
BlueFragment.java
public class BlueFragment extends Fragment {
private TextView mTextView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_blue, container, false);
mTextView = v.findViewById(R.id.textview);
return v;
}
// This is a public method that the Activity can use to communicate
// directly with this Fragment
public void youveGotMail(String message) {
mTextView.setText(message);
}
}
XML
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<!-- Green Fragment container -->
<FrameLayout
android:id="#+id/green_fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginBottom="16dp" />
<!-- Blue Fragment container -->
<FrameLayout
android:id="#+id/blue_fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
fragment_green.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#98e8ba"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button"
android:text="send message to blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
fragment_blue.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#30c9fb"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textview"
android:text="TextView"
android:textSize="24sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
The nicest and recommended way is to use a shared ViewModel.
https://developer.android.com/topic/libraries/architecture/viewmodel#sharing
From Google doc:
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
ps: two fragments never communicate directly
Consider my 2 fragments A and B, and Suppose I need to pass data from B to A.
Then create an interface in B, and pass the data to the Main Activity. There create another interface and pass data to fragment A.
Sharing a small example:
Fragment A looks like
public class FragmentA extends Fragment implements InterfaceDataCommunicatorFromActivity {
public InterfaceDataCommunicatorFromActivity interfaceDataCommunicatorFromActivity;
String data;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void updateData(String data) {
// TODO Auto-generated method stub
this.data = data;
//data is updated here which is from fragment B
}
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
try {
interfaceDataCommunicatorFromActivity = (InterfaceDataCommunicatorFromActivity) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}
}
FragmentB looks like
class FragmentB extends Fragment {
public InterfaceDataCommunicator interfaceDataCommunicator;
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
// call this inorder to send Data to interface
interfaceDataCommunicator.updateData("data");
}
public interface InterfaceDataCommunicator {
public void updateData(String data);
}
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
try {
interfaceDataCommunicator = (InterfaceDataCommunicator) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}
}
Main Activity is
public class MainActivity extends Activity implements InterfaceDataCommunicator {
public InterfaceDataCommunicatorFromActivity interfaceDataCommunicatorFromActivity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public void updateData(String data) {
// TODO Auto-generated method stub
interfaceDataCommunicatorFromActivity.updateData(data);
}
public interface InterfaceDataCommunicatorFromActivity {
public void updateData(String data);
}
}
There are multiple ways to communicate between fragments.
Traditional way of communication via interface Example
Via ViewModel if you are following MVVM pattern Example
BroadcastReceivers: via LocalBraodcastManager Example or EventBus Example etc...
Take a look at https://github.com/greenrobot/EventBus
or http://square.github.io/otto/
or even ... http://nerds.weddingpartyapp.com/tech/2014/12/24/implementing-an-event-bus-with-rxjava-rxbus/
There is a simple way to implement communication between fragments of an activity using architectural components. Data can be passed between fragments of an activity using ViewModel and LiveData.
Fragments involved in communication need to use the same view model objects which is tied to activity life cycle. The view model object contains livedata object to which data is passed by one fragment and the second fragment listens for changes on LiveData and receives the data sent from fragment one.
For complete example see http://www.zoftino.com/passing-data-between-android-fragments-using-viewmodel
Since Fragment 1.3.0 we have available a new way to communicate between fragments.
As of Fragment 1.3.0, each FragmentManager implements FragmentResultOwner.
That means that a FragmentManager can act as a central storage for fragment results. This change allows components to communicate with each other by setting chunk results and listening to those results without those components having direct references to each other.
Fragment listener:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Use the Kotlin extension in the fragment-ktx artifact
setFragmentResultListener("requestKey") { requestKey, bundle ->
// We use a String here, but any type that can be put in a Bundle is supported
val result = bundle.getString("bundleKey")
// Do something with the result
}
}
Fragment emitter:
button.setOnClickListener {
val result = "result"
// Use the Kotlin extension in the fragment-ktx artifact
setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}
Learn " setTargetFragment() "
Where " startActivityForResult() " establishes a relationship between 2 activities, " setTargetFragment() " defines the caller/called relationship between 2 fragments.
I give my activity an interface that all the fragments can then use. If you have have many fragments on the same activity, this saves a lot of code re-writing and is a cleaner solution / more modular than making an individual interface for each fragment with similar functions. I also like how it is modular. The downside, is that some fragments will have access to functions they don't need.
public class MyActivity extends AppCompatActivity
implements MyActivityInterface {
private List<String> mData;
#Override
public List<String> getData(){return mData;}
#Override
public void setData(List<String> data){mData = data;}
}
public interface MyActivityInterface {
List<String> getData();
void setData(List<String> data);
}
public class MyFragment extends Fragment {
private MyActivityInterface mActivity;
private List<String> activityData;
public void onButtonPress(){
activityData = mActivity.getData()
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof MyActivityInterface) {
mActivity = (MyActivityInterface) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement MyActivityInterface");
}
}
#Override
public void onDetach() {
super.onDetach();
mActivity = null;
}
}
You can user 2 approcach to communicate between 2 fragments:
1 )
You can use LiveData to observe data changes of one fragment in another
Create shared ViewModel
public class SharedViewModel extends ViewModel {
private MutableLiveData<String> name;
public void setNameData(String nameData) {
name.setValue(nameData);
}
public MutableLiveData<String> getNameData() {
if (name == null) {
name = new MutableLiveData<>();
}
return name;
}
}
Fragment One
private SharedViewModel sharedViewModel;
public FragmentOne() {
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
submitButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
sharedViewModel.setNameData(submitText.getText().toString());
}
});
}
Fragment Two
private SharedViewModel sharedViewModel;
public FragmentTwo() {
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
sharedViewModel.getNameData().observe(this, nameObserver);
}
Observer<String> nameObserver = new Observer<String>() {
#Override
public void onChanged(String name) {
receivedText.setText(name);
}
};
For more details on viewmodel you can refer to : mvvm-viewmodel-livedata , communicate fragments
2 )
You can use eventbus to achieve the same
implementation 'org.greenrobot:eventbus:3.2'
Define Event
public static class MessageEvent { /* Additional fields if needed */ }
Register/Unregister Subsciber
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
Listen To Events
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
Post Events
EventBus.getDefault().post(new MessageEvent());
Basically, following are the ways for communication between two fragments:
i) ViewModel
ii) Fragment Result API
iii) Interface
I use many fragments on tabs that need to share data between them, such as a ble scan tab that needs up update a device id on a settings tab.
The communication is a mess for something simple like one edittext.
My solution was to save data to sharedpreferences and use the fragment onResume to read and update.
I can extend the fields in Sharedpreferences later if I need to as well.
Update
Ignore this answer. Not that it doesn't work. But there are better methods available. Moreover, Android emphatically discourage direct communication between fragments. See official doc. Thanks user #Wahib Ul Haq for the tip.
Original Answer
Well, you can create a private variable and setter in Fragment B, and set the value from Fragment A itself,
FragmentB.java
private String inputString;
....
....
public void setInputString(String string){
inputString = string;
}
FragmentA.java
//go to fragment B
FragmentB frag = new FragmentB();
frag.setInputString(YOUR_STRING);
//create your fragment transaction object, set animation etc
fragTrans.replace(ITS_ARGUMENTS)
Or you can use Activity as you suggested in question..
I recently created a library that uses annotations to generate those type casting boilerplate code for you.
https://github.com/zeroarst/callbackfragment
Here is an example. Click a TextView on DialogFragment triggers a callback to MainActivity in onTextClicked then grab the MyFagment instance to interact with.
public class MainActivity extends AppCompatActivity implements MyFragment.FragmentCallback, MyDialogFragment.DialogListener {
private static final String MY_FRAGM = "MY_FRAGMENT";
private static final String MY_DIALOG_FRAGM = "MY_DIALOG_FRAGMENT";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction()
.add(R.id.lo_fragm_container, MyFragmentCallbackable.create(), MY_FRAGM)
.commit();
findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MyDialogFragmentCallbackable.create().show(getSupportFragmentManager(), MY_DIALOG_FRAGM);
}
});
}
Toast mToast;
#Override
public void onClickButton(MyFragment fragment) {
if (mToast != null)
mToast.cancel();
mToast = Toast.makeText(this, "Callback from " + fragment.getTag() + " to " + this.getClass().getSimpleName(), Toast.LENGTH_SHORT);
mToast.show();
}
#Override
public void onTextClicked(MyDialogFragment fragment) {
MyFragment myFragm = (MyFragment) getSupportFragmentManager().findFragmentByTag(MY_FRAGM);
if (myFragm != null) {
myFragm.updateText("Callback from " + fragment.getTag() + " to " + myFragm.getTag());
}
}
}

Categories

Resources