I'm probably doing this in the wrong place, but currently I have a DialogFragment that represents a dialog with a camera button (among other things) and I'm using the built-in camera app (calling via an Intent) that returns to the DialogFragment's onActivityResult().
This all works great, but my goal is to find an attachments LinearLayout in my dialog and essentially attach a copy of the captured image to it. e.g. you takes some pics and they appear in the dialog's "attachments" section.
The issue appears to be that when the DialogFragment's onActivityResult() is triggered, I'm unable to get the its view, it's null:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMERA_PIC_REQUEST && resultCode == Activity.RESULT_OK) {
try {
LinearLayout attachments = (LinearLayout) getView().findViewById(R.id.landmarkAttachmentView); // <- getView() is null
I threw in a debug log in a couple of suspect methods I thought might trigger when the camera returned, that had the DialogFragment's view available, but none of them logged:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "Creating landmark dialog view");
if(getView() == null) Log.d(TAG, "No view!");
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void show(FragmentManager manager, String tag) {
Log.d(TAG, "Show landmark dialog view");
if(getView() == null) Log.d(TAG, "No view!");
super.show(manager, tag);
}
#Override
public void onViewStateRestored(Bundle savedInstanceState) {
Log.d(TAG, "Restored landmark dialog view");
if(getView() == null) Log.d(TAG, "No view!");
super.onViewStateRestored(savedInstanceState);
}
public void onResume() {
Log.d(TAG, "Resume landmark dialog view");
if(getView() == null) Log.d(TAG, "No view!");
super.onResume();
}
#Override
public void onStart() {
Log.d(TAG, "Start landmark dialog view");
if(getView() == null) Log.d(TAG, "No view!");
super.onStart();
}
Only onStart and onResume triggered but this.view was null. How can I get my DialogFragment's view after it's returned from another activity/fragment?
Thanks!
You need to override onCreateView(), create your view there and return it back. Then it will be available in other methods too.
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.<your_view_layout>, container, false);
}
Here is more documentation and an example:
http://developer.android.com/reference/android/app/DialogFragment.html
Related
I'm having this error while im loading the Fragment for a second time. This is very strange because on the first time the fragment load wihout any problem. Here is the error:
java.lang.IllegalStateException: Fragment ShopFragment{8f5d575} (89cc4724-c435-49f4-ad05-4de4f1d18fc9)} not attached to an activity.
at androidx.fragment.app.Fragment.requireActivity(Fragment.java:833)
at com.example.fragmentapp.ui.fragments.ShopFragment$4.onChanged(ShopFragment.java:138)
This happens only when i open the fragment for a second time, and the app crashes. I'm starting with MVVM and Room so im not an expert on the matter so im i little lost.
Here is the code from my fragment
public class ShopFragment extends Fragment {
private ShopViewModel shopViewModel;
private Spinner spDeptoShop;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_shop, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
shopViewModel = new ViewModelProvider(requireActivity()).get(ShopViewModel.class);
spDeptoShop = (Spinner) view.findViewById(R.id.sp_deparmento_shop);
init();
subscribeObservers();
}
private void init() {
shopViewModel.getDeptosLocal();
}
private void subscribeObservers() {
shopViewModel.getDeptos().observe(requireActivity(), new Observer<Resource<List<Departamento>>>() {
#Override
public void onChanged(Resource<List<Departamento>> listResource) {
if(listResource != null){
Log.d(TAG, "onChanged: status: " + listResource.status);
if(listResource.data != null){
switch (listResource.status){
case LOADING:{
break;
}
case ERROR:{
Log.e(TAG, "onChanged: cannot refresh the cache." );
Log.e(TAG, "onChanged: ERROR message: " + listResource.message );
Log.e(TAG, "onChanged: status: ERROR, #DEPARTAMENTO: " + listResource.data.size());
break;
}
case SUCCESS:{
Log.d(TAG, "onChanged: cache has been refreshed.");
Log.d(TAG, "onChanged: status: SUCCESS, #DEPARTAMENTO: " + listResource.data.size());
if(listResource.data != null && listResource.data.size()>0)
{
spDeptoShop.setAdapter( new ArrayAdapter<Departamento>(requireActivity(), R.layout.spinner_list_item, listResource.data));
}
else{
}
break;
}
}
}
}
}
});
}
#Override
public void onPause() {
super.onPause();
shopViewModel.cancelRequest();
}
}
The error is in the line:
spDeptoShop.setAdapter( new ArrayAdapter<Departamento>(requireActivity(), R.layout.spinner_list_item, listResource.data));
You are calling observe() with requireActivity() - your Activity's Lifecycle. That means you will continue to receive results until your Activity is stopped - i.e., it isn't tied to your Fragment's lifecycle at all, meaning it will continue to receive results even after your fragment is detached. You should never use requireActivity() with observe() in a Fragment.
Fragments have a special lifecycle specifically tied to when their views are created - getViewLifecycleOwner(). This is what you should be using for your observe:
shopViewModel.getDeptos().observe(getViewLifecycleOwner(), new Observer<Resource<List<Departamento>>>() {
I know I this question maybe asked before still I wanted to put my question up for all ,I need to make a scanner feature in my application , I am using a botton navigation in my project and due to that i am making more than one fragments on the same activity . Now in that fragment I need to call an ImageButton on the click of which camera activity has to open and after clicking the picture(or scanning lets suppose a QR code) it should go to the same fragment and return the scanned value. It worked in case of new activity but not working in fragment.
this is what I am doing
public class HomeFragment extends Fragment implements View.OnClickListener {
ImageButton btnScanner;
private TextView textViewName, textViewAddress;
//qr code scanner object
private IntentIntegrator qrScan;
public HomeFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_home, container, false);
textViewName=(TextView)root.findViewById(R.id.textViewName);
textViewAddress=(TextView)root.findViewById(R.id.textViewAddress);
btnScanner=(ImageButton)root.findViewById(R.id.scannerBtn);
btnScanner.setOnClickListener(this);
qrScan = new IntentIntegrator(getActivity());
return root;
}
private void initViews(){
}
//Getting the scan results
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (result != null) {
//if qrcode has nothing in it
if (result.getContents() == null) {
Toast.makeText(getContext(), "Result Not Found", Toast.LENGTH_LONG).show();
} else {
//if qr contains data
try {
//converting the data to json
JSONObject obj = new JSONObject(result.getContents());
//setting values to textviews
textViewName.setText(obj.getString("name"));
textViewAddress.setText(obj.getString("address"));
} catch ( JSONException e) {
e.printStackTrace();
//if control comes here
//that means the encoded format not matches
//in this case you can display whatever data is available on the qrcode
//to a toast
Toast.makeText(getContext(), result.getContents(), Toast.LENGTH_LONG).show();
}
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
#Override
public void onClick(View view) {
qrScan.initiateScan();
}
}
Here I am using the instance of a library class intentIntegrator.
I have also vited to
https://stackoverflow.com/questions/44622311/how-can-i-call-onactivityresult-inside-fragment-and-how-it-work/44622487#:~:text=if%20you%20call%20startActivityForResult(),deliver%20result%20to%20desired%20fragment.%20%7D&text=super.,-onActivityResult%20()%20will and
onActivityResult is not being called in Fragment yet my problem is not resolved
I'm working on a fragment which displays a list of images. It has a button for "add image" that starts an intent for result with these values.
type: "image/*"
action: Intent.ACTION_GET_CONTENT
The problem is: after the user picks an image and returns to the fragment, all the other images in the list (stored in some ArrayList<> on the code) are gone.
I've overridden the method onSaveInstanceState(Bundle) and saved the list inside the bundle. The thing is, there's no way to restore it back.
I thought of overriding onViewStateRestored(Bundle) but it didn't work. When I put some Log.d() on all "onXXX" methods, I found that only these three are executed every time I add a file (actual order):
onPause()
onSaveInstanceState(Bundle)
//now the image picker opens up
//user picks the image
onResume()
//image picker closes and fragment is now on screen
I thought of using some "getXXX" method at onResume() but I can't find one that's useful. What can I do?
EDIT: Here is my code (without irrelevant stuff).
#Override public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Log.d("debugaff", "oncreate");
setRetainInstance(true);
this.attachments = (ArrayList<Attachment>) getActivity().getIntent().getSerializableExtra(ExtraKeys.ATTACHMENTS);
}
#Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
Log.d("debugaff", "oncreateview");
rootView = inflater.inflate(R.layout.fragment_attachments_form, container, false);
//....
if(savedInstanceState == null){
getLoaderManager().initLoader(0, null, this);
} else {
attachmentsRvAdapter.setItems((List<Attachment>) savedInstanceState.getSerializable(ExtraKeys.TEMP_ATTACHMENTS));
}
//....
return rootView;
}
#Override public void onResume(){
super.onResume();
Log.d("debugaff", "onresume");
hideKeyboard();
}
#Override public void onPause(){
super.onPause();
setRetainInstance(true); //called this to make it 100% redundant (already called it at onCreate)
Log.d("debugaff", "onpause");
}
#Override public void onViewStateRestored(#Nullable Bundle savedInstanceState){
Log.d("debugaff", "onviewstaterestored");
if(savedInstanceState == null){
getLoaderManager().initLoader(0, null, this);
} else {
attachments.addAll((List<Attachment>) savedInstanceState.getSerializable(ExtraKeys.TEMP_ATTACHMENTS));
attachmentsRvAdapter.setItems(attachments);
}
super.onViewStateRestored(savedInstanceState);
}
I have just mimic your situation and found no error (list saves the new value with old values in the list). The only problem I can think of is you are not initializing your list correctly. What I did was to initialized the list in the onCreate() method and when I pick another Image it saves that image in the same list with the previous image still in the list. There is no need to save the list separately in the savedInstanceBundle. Here is my Fragment code:
public class SelectListFragment extends Fragment {
private static final int PICK_IMAGE_REQUEST = 11;
public SelectListFragment() {
// Required empty public constructor
}
List listOfImagesSelected;
Button selectButton;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listOfImagesSelected=new ArrayList<Bitmap>();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_select_list, container, false);
selectButton = view.findViewById(R.id.selectButton);
selectButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE_REQUEST);
}
});
return view;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//I am retrieving the Bitmap from the intent and saving into the list
if (requestCode == PICK_IMAGE_REQUEST && resultCode == Activity.RESULT_OK && data != null && data.getData() != null) {
Uri uri = data.getData();
try {
//making the bitmap from the link of the file selected
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), uri);
listOfImagesSelected.add(bitmap);
}catch (Exception e){
}
}
}
}
I can share the Complete Working Code of my Project if needed. For better understanding of the Fragment LifeCycle please review this Image
To use onViewStateRestored, you need to call 'setRetainInstance(true);'
I'm trying to hook up Spotify with my Android app. I want to be able to hit a button and have the user be able to start playing from their library on the 4th tab of the TabLayout, and have the ability to swipe back and forth from one tab to another without the music stopping. Here is my current view:
Tabbed View
When I hit start broadcast, I just have it hooked up to SpotifyActivity.java where I've logged in and it starts playing a song. However, I want to have it so that I don't have to invoke a new activity when I want to start accessing my library and playing music so that I can switch between tabs and use the rest of the functionality of the app while the music plays. I recently learned about Services in Android and I was wondering if there was a way to use that. I basically want the flow to be like this:
The 4th tab presents a "start broadcast" button. Once this is hit, you log in to Spotify (or continue if you're already logged in), and a new view (activity?) pops up that gets Spotify and a UI working. Once the music starts, I can switch back to any of the first three tabs and interact with those while the music plays, and then when I go back to the first tab the activity pops up again with the music player UI and Spotify stuff. Here is my code for PageFragment:
public class PageFragment extends Fragment {
public static final String ARG_PAGE = "ARG_PAGE";
private static User thisUser;
private int mPage;
public static PageFragment newInstance(int page, User localUser) {
Bundle args = new Bundle();
thisUser = localUser;
args.putInt(ARG_PAGE, page);
PageFragment fragment = new PageFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPage = getArguments().getInt(ARG_PAGE);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = null;
TextView textView;
if (mPage == 1) {
//inflate the fragment_home_page
v = inflater.inflate(R.layout.fragment_home_page, container, false);
//handle profile and username information
}
else if (mPage == 2) {
//inflate the fragment_play_page
v = inflater.inflate(R.layout.fragment_play_page, container, false);
//handle music library information
}
else if(mPage == 3) {
//inflate the fragment_live_page
v = inflater.inflate(R.layout.fragment_live_page, container, false);
//handle live information
}
else if (mPage == 4) {
//inflate the fragment_nowplaying_page
v = inflater.inflate(R.layout.fragment_nowplaying_page, container, false);
Button startBroadcast = (Button) v.findViewById(R.id.start_broadcast);
//Links to SpotifyActivity
startBroadcast.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), SpotifyActivity.class);
startActivity(intent);
}
});
}
else {
Log.e("PageFragment ", "Unknown error occurred");
}
return v;
}
}
Here is my code for SpotifyActivity:
public class SpotifyActivity extends AppCompatActivity implements SpotifyPlayer.NotificationCallback, ConnectionStateCallback {
//CLIENT_ID=
//Callback
// Request code that will be used to verify if the result comes from correct activity
// Can be any integer
private static final int REQUEST_CODE = 1337;
private Player mPlayer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_spotify);
AuthenticationRequest.Builder builder = new AuthenticationRequest.Builder(CLIENT_ID,
AuthenticationResponse.Type.TOKEN,
REDIRECT_URI);
builder.setScopes(new String[]{"user-read-private", "streaming"});
AuthenticationRequest request = builder.build();
AuthenticationClient.openLoginActivity(this, REQUEST_CODE, request);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
//After the user logs in to authorize the application's scopes
// Check if result comes from the correct activity
if (requestCode == REQUEST_CODE) {
AuthenticationResponse response = AuthenticationClient.getResponse(resultCode, intent);
if (response.getType() == AuthenticationResponse.Type.TOKEN) {
Config playerConfig = new Config(this, response.getAccessToken(), CLIENT_ID);
Spotify.getPlayer(playerConfig, this, new SpotifyPlayer.InitializationObserver() {
#Override
public void onInitialized(SpotifyPlayer spotifyPlayer) {
mPlayer = spotifyPlayer;
mPlayer.addConnectionStateCallback(SpotifyActivity.this);
mPlayer.addNotificationCallback(SpotifyActivity.this);
}
#Override
public void onError(Throwable throwable) {
Log.e("MainActivity", "Could not initialize player: " + throwable.getMessage());
}
});
}
}
}
#Override
protected void onDestroy() {
Spotify.destroyPlayer(this);
super.onDestroy();
}
#Override
public void onPlaybackEvent(PlayerEvent playerEvent) {
Log.d("MainActivity", "Playback event received: " + playerEvent.name());
switch (playerEvent) {
// Handle event type as necessary
default:
break;
}
}
#Override
public void onPlaybackError(Error error) {
Log.d("MainActivity", "Playback error received: " + error.name());
switch (error) {
// Handle error type as necessary
default:
break;
}
}
#Override
public void onLoggedIn() {
Log.d("MainActivity", "User logged in");
mPlayer.playUri(null, "spotify:track:2TpxZ7JUBn3uw46aR7qd6V", 0, 0);
}
#Override
public void onLoggedOut() {
Log.d("MainActivity", "User logged out");
}
#Override
public void onLoginFailed(Error error) {
Log.d("MainActivity", "Login failed");
}
#Override
public void onTemporaryError() {
Log.d("MainActivity", "Temporary error occurred");
}
#Override
public void onConnectionMessage(String s) {
Log.d("MainActivity", "Received connection message: " + s);
}
}
Is there a way to set up Spotify's music player without losing it when I switch to other tabs?
I want to create simple communication Listener between Fragment and Activity, after define listener in Activity and implemented that from Fragment I get NullPointerException when I send String from Activity to Fragment, I want to send data from onActivityResult.
My activity is:
public class ActivityMain extends FragmentActivity {
public IonGetBarcodeFromScannerListener ionGetBarcodeFromScannerListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
G.currentActivity = this;
G.context = getBaseContext();
}
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (scanningResult != null) {
String scanContent = scanningResult.getContents();
String scanFormat = scanningResult.getFormatName();
Log.e("barcode is: ", scanContent);
doSendBarcode(scanContent);
}
else{
Log.e("No scan data received! ", "");
doSendBarcode("");
}
}
public void doSendBarcode(String barcode){
ionGetBarcodeFromScannerListener.getBarcode(barcode);
}
public void setBarcodeListener(IonGetBarcodeFromScannerListener l){
ionGetBarcodeFromScannerListener = l;
}
public interface IonGetBarcodeFromScannerListener {
public void getBarcode(String barcode);
}
}
i get NullPointerException for this line:
ionGetBarcodeFromScannerListener.getBarcode(barcode);
My Fragment:
public class FragmentAddNewWayBill extends Fragment implements ActivityMain.IonGetBarcodeFromScannerListener{
private PtrClassicFrameLayout mPtrFrame;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
G.currentActivity = FragmentAddNewWayBill.this.getActivity();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_new_waybill, container, false);
return view;
}
#Override
public void getBarcode(String barcode) {
Toast.makeText(G.currentActivity, "salam: " + barcode, Toast.LENGTH_SHORT).show();
}
}
I guess you are trying for activity and fragment communication in opposite way as I think interface will be defined in fragment and it's implementation will be done in activity. See http://developer.android.com/training/basics/fragments/communicating.html for more details
Seems you are never calling setBarcodeListener() so the field is never set, and therefore null.
-- update --
You could call it inside the fragment's onAttach() by using the return value from .getActivity().