I am developing an android application in which open camera in a fragment. Whenever Camera is opened for the FIRST TIME. It loads with a small jerk of 1 second approximately. Making screen black. How to prevent screen from turning black for that second completely.
Detailed Explanation:
When we open camera in Facebook messenger or even try to open camera normally in your phone. It takes one second to open and meanwhile screens turns black. The same thing is happening. Can this be prevented? Any how? your reply will be highly appreciated Please guys.
Below Is the working code with same problem as described above.
public class scan extends Fragment implements ZXingScannerView.ResultHandler{
private ZXingScannerView zXingScannerView;
private SurfaceView mySurfaceView;
private QREader qrEader;
private Camera mCamera;
private CameraPreview mPreview;
private String m_Text="";
private String number="";
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private String mParam1;
private String mParam2;
private OnFragmentInteractionListener mListener;
public scan() {
}
#Override
public void handleResult(Result rawResult) {
Log.e("handler", rawResult.getText());
Log.e("handler", rawResult.getBarcodeFormat().toString());
AlertDialog.Builder builder = new AlertDialog.Builder(this.getActivity());
builder.setTitle("Scan Result");
builder.setMessage(rawResult.getText());
number = rawResult.getText().substring(rawResult.getText().length() - 13);
//
final EditText input = new EditText(this.getActivity());
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
builder.setView(input);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
m_Text = input.getText().toString();
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setNegativeButton("NO",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog alert1 = builder.create();
alert1.show();
zXingScannerView.resumeCameraPreview(this);
}
public static scan newInstance(String param1, String param2) {
scan fragment = new scan();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
if(checkCameraHardware(getActivity().getApplicationContext())) {
zXingScannerView = new ZXingScannerView(this.getActivity().getApplicationContext());
zXingScannerView.setResultHandler(this);
zXingScannerView.startCamera();
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_scan, container, false);
FrameLayout preview =(FrameLayout)view.findViewById(R.id.camera_preview);
preview.addView(zXingScannerView);
return view;
}
#Override
public void onPause()
{
super.onPause();
zXingScannerView.stopCamera();
}
#Override
public void onResume() {
super.onResume();
zXingScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
zXingScannerView.startCamera(); // Start camera on resume
}
/** Check if this device has a camera */
public boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
if (mCamera!= null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
super.onDetach();
mListener = null;
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}
I see that you use the deprecated Camera API. If you want your app to perform the best on devices with Lollipop or higher, you should switch to the new camera2 API. Please see the discussion here: Android camera android.hardware.Camera deprecated.
The new API can improve performance significantly, but if you are stuck with the old one, don't despair. If you are not using the latest version of ZXingScannerView, update this class to open the camera in a background thread. This change improved startup significantly.
If your fragment is part of a ViewPager (like camera preview within viewpager), make sure that the camera preview is started before the pager brings the scan fragment to screen.
In other scenarios, it is preferable to go for a variation of splash screen paradigm, which has it pros and cons. You can show for a short while another non-black view on top of the camera preview surface, or show a predefined texture if you use OpenGL preview.
Try this below code to open camera and click picture as it works for me.
public class CameraImage extends Fragment {
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 1888;
Button button;
ImageView imageView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.camera_image,
container, false);
button = (Button) rootView.findViewById(R.id.button);
imageView = (ImageView) rootView.findViewById(R.id.imageview);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent,
CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
});
return rootView;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
Bitmap bmp = (Bitmap) data.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
// convert byte array to Bitmap
Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0,
byteArray.length);
imageView.setImageBitmap(bitmap);
}
}
}
}
Hope it will work for you.
Related
I use Navigation component jetpak and in fragment need to get image from gallery.
i use this code:
ActivityResultLauncher<String> mGetContent = registerForActivityResult(new ActivityResultContracts.GetContent(),
new ActivityResultCallback<Uri>() {
#Override
public void onActivityResult(Uri uri) {
Constants.toast("return!");
imageFile = new File(getRealPathFromURI(uri));
binding.menuFragmentCircularProfileImageview.setImageURI(uri);
}
});
and after fragment attached call mGetContent
binding.menuFragmentCircularProfileImageview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mGetContent.launch("image/*");
// fromGallery();
}
});
So far everything is fine and the gallery opens well.
BUT...
But the selected image does not return.
The app is actually closed.
Where did I do wrong? Or is there another solution?
UPDATED...
this is my fragment:
public class MenuFragment extends Fragment implements LogoutDialog.Listener {
private FragmentMenuBinding binding;
private NavController navController = null;
private UserData userData;
private File imageFile;
private final androidx.activity.result.ActivityResultLauncher<String> getContent = registerForActivityResult(new ActivityResultContracts.GetContent(),
new ActivityResultCallback<Uri>() {
#Override
public void onActivityResult(Uri uri) {
Bitmap bitmap = null;
try {
bitmap = MediaStore.Images.Media.getBitmap(requireActivity().getContentResolver(), uri);
} catch (IOException e) {
e.printStackTrace();
}
binding.menuFragmentCircularProfileImageview.setImageBitmap(bitmap);
}
});
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
navController = Navigation.findNavController(requireActivity(), R.id.main_activity_nav_host_fragment);
binding = DataBindingUtil.inflate(inflater , R.layout.fragment_menu, container, false);
return binding.getRoot();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
assert getArguments() != null;
userData=MenuFragmentArgs.fromBundle(getArguments()).getUserdata();
operation();
}
private void operation(){
binding.menuFragmentCircularProfileImageview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getContent.launch("image/*");
}
});
}
}
Try using below code:
private ActivityResultLauncher startForResultFromGallery = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK){
try {
if (result.getData() != null){
Uri selectedImageUri = result.getData().getData();
Bitmap bitmap = BitmapFactory.decodeStream(getBaseContext().getContentResolver().openInputStream(selectedImageUri));
// set bitmap to image view here........
binding.menuFragmentCircularProfileImageview.setImageBitmap(bitmap)
}
}catch (Exception exception){
Log.d("TAG",""+exception.getLocalizedMessage());
}
}
}
});
And call above inside your button click like :
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startForResultFromGallery.launch(intent);
You can use this inside your onActivityResult to get the file
InputStream inputStream = requireActivity().contentResolver().openInputStream(uri)
String fileType = MimeTypeMap.getSingleton().getExtensionFromMimeType(requireContext().contentResolver().getType(uri))
imageFile = File.createTempFile(UUID.randomUUID().toString(), "." + fileType)
inputStream.copyStreamToFile(file)
I have ported this code from Kotlin let me know if some syntax is wrong.
I believe you have android:noHistory="true" in your AndroidManifest.xml, please remove it, and you should receive the result. I believe the problem has no relation with Jetpack navigation.
In fragment'A' when a certain condition is established, it should go to 'ChatActivity'.
Intent intent = new Intent(getContext(), ChatActivity.class);
intent.addFlags(FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("uid", matchedUid);
startActivity(intent);
The problem is when other fragment or Activity is showing on the top, the condition is established so startActivity is not executed.. occur an error
'java.lang.String android.content.Context.getPackageName()' on a null object reference
below is entire code
public class MatchFragment extends Fragment implements MatchMVP.View {
private static final String TAG = "MatchFragment";
private MatchPresenter matchPresenter;
private ToggleButton randomMatchBtn;
private ProgressBar progressBar, progressCircle;
private TextView searchingText;
private AdView adView;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v;
setupMVP();
if(matchPresenter.checkOnlineStatus(getContext())) {
v = inflater.inflate(R.layout.fragment_match, container, false);
setupView(v);
matchPresenter.isSearching();
initAd(v);
} else {
v = inflater.inflate(R.layout.fragment_offline, container, false);
}
return v;
}
private void setupMVP() {
matchPresenter = new MatchPresenter(this);
}
private void setupView(View v) {
progressBar = v.findViewById(R.id.progressbar);
progressCircle = v.findViewById(R.id.progressbar_circle);
searchingText = v.findViewById(R.id.searching_text);
progressBar.setVisibility(View.INVISIBLE);
progressCircle.setVisibility(View.INVISIBLE);
searchingText.setVisibility(View.INVISIBLE);
randomMatchBtn = v.findViewById(R.id.random_match_btn);
randomMatchBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(matchPresenter.checkOnlineStatus(getContext())) {
if(randomMatchBtn.isChecked()) {
matchPresenter.searchRandomUser();
progressBar.setVisibility(View.VISIBLE);
} else {
matchPresenter.stopMatch();
progressBar.setVisibility(View.VISIBLE);
}
} else {
showSnackBar("error");
}
}
});
}
private void initAd(View v) {
MobileAds.initialize(getActivity(), "ca-app-pub-6263138384822549~5566878684");
adView = v.findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder().build();
adView.loadAd(adRequest);
}
#Override
public void createChatRoom(String matchedUid) {
Intent intent = new Intent(getContext(), ChatActivity.class);
intent.addFlags(FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("uid", matchedUid);
startActivity(intent);
Vibrator vibrator;
if(getContext() != null) {
vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(700);
}
}
#Override
public void showSnackBar(String msg) {
Snackbar snackbar = Snackbar.make(getActivity().findViewById(android.R.id.content), msg, 2500);
View snackBarLayout = snackbar.getView();
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
// Layout must match parent layout type
lp.setMargins(0, 300, 0, 0);
// Margins relative to the parent view.
snackBarLayout.setLayoutParams(lp);
snackbar.show();
}
#Override
public void randomMatchBtnOff() {
randomMatchBtn.setChecked(false);
}
#Override
public void randomMatchBtnDisable() {
randomMatchBtn.setEnabled(false);
}
#Override
public void randomMatchBtnEnable() {
randomMatchBtn.setEnabled(true);
progressBar.setVisibility(View.INVISIBLE);
}
#Override
public void showProgressCircle() {
progressCircle.setVisibility(View.VISIBLE);
searchingText.setVisibility(View.VISIBLE);
}
#Override
public void hideProgressCircle() {
progressCircle.setVisibility(View.INVISIBLE);
searchingText.setVisibility(View.INVISIBLE);
}
#Override
public void goAuthActivity() {
Intent intent = new Intent(getContext(), AuthActivity.class);
intent.putExtra("isSanctioned", true);
startActivity(intent);
assert getActivity() != null;
getActivity().finish();
}
#Override
public void onResume() {
super.onResume();
matchPresenter.checkIsSan();
}
#Override
public void onPause() {
super.onPause();
if(isThreadRunning) {
timeCheckThread.interrupt();
}
}
}
Cause of error is 'Fragment is not attached to its Activity'. Yeah I know. I'm making a randomChatting app with firebase. In this MatchFragment, I'm searching other users. When other users start searching, matched with me then let me know by go to 'ChatActivity'.
But if I'm in other fragment of activity, searching is activating, it can't go ChatActivity. 'Fragment is not attached to its Activity'.
Because I'm in other activity not in this MatchFragment. MatchFragment detached to its Activity.
How go to ChatActivity even if I'm in other activity.
You can either try using requireActivity() instead of getActivity() but since you only need a Context object and not necessarily an Activity object, I suggest you replace getActivity() with requireContext().
If that doesn't work out then you can try following this answer: https://stackoverflow.com/a/30498143
PS: I know this should be shared as a comment but my reputation is currently only 41 and I can't post a comment so writing this as an answer.
I'm developing an application where I read some barcode. In a first step I had a big SurfaceView where I can see well the camera preview, but now I would set the dimensions of Surfaceview like the dimensions of barcode but I have bad camera visualization (it is too small). Can someone help me to stretch camera preview? Thanks
Here manage detector and surfaceview:
public class LettoreBarcode extends Fragment {
View view;
SurfaceView surfaceView;
Handler handler;
private BarcodeDetector detector;
private CameraSource cameraSource;
private TextView code;
SparseArray<Barcode> items;
private static Button btnBack;
String barcode = "" ;
SparseArray<Articoli> groups = new SparseArray<Articoli>();
Context _context = null;
ProductsAdapter.ViewHolder _ViewHolder = null;
public LettoreBarcode(){
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_barcode_scanner, container, false);
surfaceView = (SurfaceView) view.findViewById(R.id.surfaceView);
detector = new BarcodeDetector.Builder(getActivity()).setBarcodeFormats(Barcode.ALL_FORMATS).build();
final Dialog d = new Dialog(getActivity());
btnBack = (Button) view.findViewById(R.id.btnBack);
handler = new Handler();
if(!detector.isOperational()){
Toast.makeText(getActivity(), "Detector non attivabile", Toast.LENGTH_LONG).show();
}
cameraSource = new CameraSource.Builder(getActivity(), detector).setAutoFocusEnabled(true).build();
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
AttivaCamera();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
detector.setProcessor(new Detector.Processor<Barcode>() {
#Override
public void release() {
}
#Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
items = detections.getDetectedItems();
if (items.size() > 0){
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if (items.valueAt(0) != null){
//do something
handler.postDelayed(new Runnable() {
#Override
public void run() {
DisattivaCamera();
}
},10); //1000
}else
{
d.setContentView(R.layout.dialog_barcode_assente);
d.setTitle("Scanner");
d.setCancelable(true);
d.show();
DisattivaCamera();
}
}
});
}
}
});
btnBack.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getActivity().onBackPressed();
}
});
return view;
}
private void AttivaCamera()
{
try{
cameraSource.start(surfaceView.getHolder());
}catch(IOException e){
Toast.makeText(getActivity(), "Errore nell'avvio della fotocamera", Toast.LENGTH_LONG).show();
}
}
private void DisattivaCamera()
{
cameraSource.stop();
}
}
It is how I visualize camera with small surfaceview:
https://i.stack.imgur.com/TMunJ.png
I'm new in android development so I'm sorry if could be a lot of mistake in the code.
Sorry for my english also..
Thanks you guys!
In order to display only part of the camera input, i.e. to crop it on the screen, you need a surface view that has dimensions that fit the camera frame aspect ratio, and overlay it with some nontransparent views to leave only part of it visible. Don't put the SurfaeView inside scrolling layout:
So, instead of
<SurfaceView width: match_parent height: 400dp />
you need e.g. FrameLayout as explained here: Is it possible to crop camera preview?
This will not change the frame that arrives to the barcode detector. But this should not worry you; the detector will handle the uncropped image correctly.
I want to create a DialogFragment subclass that can be reused accross my app. So when an Activity wants to create a DialogFragment, it can set its own texts and attach its own listeners for the positive and negative buttons. For the texts inside the DialogFragment, I pass them to the fragment using the arguments bundle to make sure they are persisted when the configuration changes. However, the listeners for the buttons cannot be passed to the fragment with these arguments.
What would be best practice to attach these listeners to the DialogFragment, without losing them when the configuration changes?
BaseDialogFragment.java
public abstract class BaseDialogFragment extends AppCompatDialogFragment {
public AppCompatDialog dialog;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(getLayoutResource(), null);
ButterKnife.bind(this, view);
return view;
}
#Override
public void onStart() {
super.onStart();
dialog = (AppCompatDialog) getDialog();
if (dialog != null) {
WindowManager windowManager =
(WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
dialog.getWindow().setLayout(width - 75, ViewGroup.LayoutParams.WRAP_CONTENT);
WindowManager.LayoutParams params = dialog.getWindow().getAttributes();
dialog.getWindow().setAttributes(params);
dialog.getWindow().setBackgroundDrawable(ContextCompat.getDrawable(getContext(), R.drawable.dialog_rounded_back));
}
}
protected abstract int getLayoutResource();
#Override
public void show(FragmentManager manager, String tag) {
try {
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commitAllowingStateLoss();
} catch (IllegalStateException e) {
}
}
}
Child fragment dialog:
public class InvitationAcceptRejectDialog extends BaseDialogFragment {
public InvitationAcceptRejectDialog() {
}
#Override
protected int getLayoutResource() {
return R.layout.invite_accept_reject_dialog;
}
protected OnDialogClickListener alertListener;
#BindView(R.id.tvDialogTitle)
AppCompatTextView tvDialogTitle;
#BindView(R.id.tvDialogMessage)
AppCompatTextView tvDialogMessage;
int requestCode;
public String dialogTitle;
public String dialogMessage;
public Bundle bundle;
#OnClick({R.id.imgCloseDialog, R.id.btnYes, R.id.btnNo})
public void dialgClick(View view) {
switch (view.getId()) {
case R.id.imgCloseDialog:
break;
case R.id.btnYes:
alertListener.onPositiveClick(dialog, requestCode, bundle);
break;
case R.id.btnNo:
alertListener.onNegativeClick(dialog, requestCode, bundle);
break;
}
dialog.dismiss();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
tvDialogTitle.setText(dialogTitle);
tvDialogMessage.setText(dialogMessage);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
public static class Builder {
InvitationAcceptRejectDialog alertDialogFragment;
public Builder() {
alertDialogFragment = new InvitationAcceptRejectDialog();
}
public Builder setTitle(String title) {
alertDialogFragment.dialogTitle = title;
return this;
}
public Builder setMessage(String message) {
alertDialogFragment.dialogMessage = message;
return this;
}
public Builder setBundel(Bundle bundel) {
alertDialogFragment.bundle = bundel;
return this;
}
public Builder setCallback(OnDialogClickListener mListener, int code) {
alertDialogFragment.alertListener = mListener;
alertDialogFragment.requestCode = code;
return this;
}
public InvitationAcceptRejectDialog build() {
return alertDialogFragment;
}
}
}
Implementation in activity or fragmnet:
InvitationAcceptRejectDialog build = new InvitationAcceptRejectDialog.Builder()
.setCallback(this, Constant.DialogConstant.ACCEPET_INVITE)
.setTitle(getString(R.string.logout))
.setMessage(getString(R.string.logout_message))
.build();
build.show(getSupportFragmentManager(), "TAG");
Interface for handle positive and negative button click:
public interface OnDialogClickListener {
void onPositiveClick(DialogInterface dialog, int id, Bundle bundle);
void onNegativeClick(DialogInterface dialog, int id, Bundle bundle);
}
I would do something like this, Use buttons with as interfaces and you can call this class any where you want in your project. and you can save it's instance too on configuration change :
public class MyDialogFragment extends DialogFragment {
// the fragment initialization parameters,
private static final String DIALOG_TITLE = "DIALOG_TITLE";
private static final String DIALOG_MESSAGE = "DIALOG_MESSAGE";
private static final String DIALOG_BUTTON_POSITIVE = "DIALOG_BUTTON_POSITIVE";
private static final String DIALOG_BUTTON_NEGATIVE = "DIALOG_BUTTON_NEGATIVE";
private String Title;
private String Message;
private String btnPositive;
private String btnNegative;
public interface DialogFragmentButtonPressedListener {
void onPositiveButtonClick();
void onNegativeButtonClick();
}
public static MyDialogFragment newInstance(String title, String message, String btnPositiveText, String btnNegativeText) {
MyDialogFragment fragment = new MyDialogFragment();
Bundle args = new Bundle();
args.putString(DIALOG_TITLE, title);
args.putString(DIALOG_MESSAGE, message);
args.putString(DIALOG_BUTTON_POSITIVE, btnPositiveText);
args.putString(DIALOG_BUTTON_NEGATIVE, btnNegativeText);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
Title = getArguments().getString(DIALOG_TITLE);
Message = getArguments().getString(DIALOG_MESSAGE);
btnPositive = getArguments().getString(DIALOG_BUTTON_POSITIVE);
btnNegative = getArguments().getString(DIALOG_BUTTON_NEGATIVE);
}
}
// updated this method. before update it was onAttach(Activity activity)
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (!(context instanceof DialogFragmentButtonPressedListener)) {
throw new ClassCastException(context.toString() + " must implement DialogFragmentButtonPressedListener");
}
}
static Handler handler = new Handler(Looper.getMainLooper());
final Runnable runnable = new Runnable( ) {
#Override
public void run() {
if (mAlertDialog.isShowing()) {
mAlertDialog.dismiss();
}
}
};
AlertDialog mAlertDialog = null;
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// return new AlertDialog.Builder(getActivity())
// .setTitle(Title)
// .setMessage(Message)
// .setPositiveButton(btnPositive, new DialogInterface.OnClickListener() {
//
// #Override
// public void onClick(DialogInterface dialog, int which) {
// ((DialogFragmentButtonPressedListener) getActivity()).onPositiveButtonClick();
// }
// })
// .setNegativeButton(btnNegative, new DialogInterface.OnClickListener() {
//
// #Override
// public void onClick(DialogInterface dialog, int which) {
// ((DialogFragmentButtonPressedListener) getActivity()).onNegativeButtonClick();
// }
// })
// .create();
return mAlertDialog;
}
}
And in my calling activity I would do like this:
new MyDialogFragment();
myDialogFragment = MyDialogFragment.newInstance("successfull", "Please follow the instructions", " OK ", "negativeButtonText");
myDialogFragment.show(getSupportFragmentManager(), "MyDialogFragment");
Regarding passing the listeners, you can create an interface with two functions one each for the positive and the negative button in your DialogFragment. Inside click listeners of your positive and negative buttons, you can call these interface methods accordingly. Create a method inside your DialogFragment to set this Interface.
I have an activity that contains a View Pager that has an adapter FragmentStatePagerAdapter.
each time enter the activity it will take up 200mb of memory, after going back out of the activity(finish()) and then re entering it it will append and double the memory used on the phone.
After troubleshooting the problem it seems as if the fragment manager is not releasing the fragments although im trying to remove them but its just not working.
I tried emptying the fragment that is being added to make sure its not something internal inside the fragment the the problem remains.
my adapter code is
private class ChildrenPagerAdapter extends FragmentStatePagerAdapter
{
private List<ChildBean> childrenBean;
public ChildrenPagerAdapter(FragmentManager fm, List<ChildBean> bean)
{
super(fm);
this.childrenBean = bean;
}
#Override
public int getItemPosition(Object object)
{
return PagerAdapter.POSITION_NONE;
}
#Override
public Fragment getItem(int position)
{
ReportFragment reportFragment = new ReportFragment();
reportFragment.childBean = childrenBean.get(position);
reportFragment.position = position;
reportFragment.mPager = mPager;
if(position == 0)
{
reportFragment.mostLeft = true;
}
if(position == childrenNumber - 1)
{
reportFragment.mostRight = true;
}
return reportFragment;
}
#Override
public int getCount()
{
return childrenNumber;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object)
{
// TODO Auto-generated method stub
super.destroyItem(container, position, object);
}
}
my activity code is
public class ReportActivity extends CustomActivity
{
public ImageLoader imageLoader;
private ViewPager mPager;
private PagerAdapter mPagerAdapter;
private int childrenNumber;
private int currentChild;
#Override
protected void onDestroy()
{
mPager.removeAllViews();
mPager.removeAllViewsInLayout();
mPager.destroyDrawingCache();
mPagerAdapter = null;
mPager = null;
System.gc();
super.onDestroy();
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setCustomTitle(string.title_activity_reports);
this.currentChild = getIntent().getIntExtra("itemselected", -1);
getSupportFragmentManager().
}
#Override
protected void onResume()
{
super.onResume();
mPager = (ViewPager) findViewById(R.id.vpchildren);
mPager.setOffscreenPageLimit(6);
childrenNumber = MainActivity.bean.size();
mPagerAdapter = new ChildrenPagerAdapter(getSupportFragmentManager(), MainActivity.bean);
mPager.setAdapter(mPagerAdapter);
mPager.setCurrentItem(currentChild);
}
}
Fragment code :
public class ReportFragment extends Fragment
{
public ChildBean childBean;
public int position;
public ImageView img;
public ImageLoader imageLoader;
public DisplayImageOptions options;
private int pee = 0;
private int poop = 0;
private double sleep = 0.0;
public ViewPager mPager;
public boolean mostLeft = false;
public boolean mostRight = false;
public ReportFragment()
{
}
#Override
public void onDestroyView()
{
super.onDestroyView();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.report_fragment, container, false);
if(mostLeft)
{
rootView.findViewById(id.btnleft).setVisibility(View.GONE);
}
if(mostRight)
{
rootView.findViewById(id.btnright).setVisibility(View.GONE);
}
rootView.findViewById(id.btnleft).setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
});
rootView.findViewById(id.btnright).setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
mPager.setCurrentItem(mPager.getCurrentItem() + 1);
}
});
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH);
Date dobchild = new Date();
((TextView) rootView.findViewById(id.tvday)).setText(sdf.format(dobchild));
ImageView childimg = (ImageView) rootView.findViewById(id.img_child);
((TextView) rootView.findViewById(id.tvchildname)).setText(childBean.childname);
((TextView) rootView.findViewById(id.tvclassname)).setText(((CustomApplication) getActivity().getApplication()).preferenceAccess.getCurrentClassName());
Date dob = null;
String age = "";
try
{
dob = sdf.parse(childBean.childdob);
age = GeneralUtils.getAge(dob.getTime(), getString(string.tv_day), getString(string.tv_month), getString(string.tv_year));
}
catch(ParseException e)
{
// TODO:
}
((CustomTextView) rootView.findViewById(id.tvchildage)).setText(age);
DisplayImageOptions options =
new DisplayImageOptions.Builder().showImageForEmptyUri(drawable.noimage).showImageOnFail(drawable.noimage).showStubImage(drawable.noimage).cacheInMemory()
.imageScaleType(ImageScaleType.NONE).build();
imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(childBean.childphoto, childimg, options);
final TextView tvpee = (TextView) rootView.findViewById(id.tvpeetime);
final TextView tvpoop = (TextView) rootView.findViewById(id.tvpootimes);
final TextView tvsleep = (TextView) rootView.findViewById(id.tvsleeptime);
rootView.findViewById(id.btnaddpee).setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
pee = pee + 1;
if(pee > 9)
{
Toast.makeText(getActivity(), getString(string.tvareyousurepee), Toast.LENGTH_LONG).show();
}
tvpee.setText(String.format(getString(string.tvtimes), pee));
}
});
rootView.findViewById(id.btnminuspee).setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
if(pee > 0)
{
pee = pee - 1;
tvpee.setText(String.format(getString(string.tvtimes), pee));
}
}
});
rootView.findViewById(id.btnpluspoo).setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
poop = poop + 1;
if(poop > 9)
{
Toast.makeText(getActivity(), getString(string.tvareyousurepoop), Toast.LENGTH_LONG).show();
}
tvpoop.setText(String.format(getString(string.tvtimes), poop));
}
});
rootView.findViewById(id.btnminuspoo).setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
if(poop > 0)
{
poop = poop - 1;
tvpoop.setText(String.format(getString(string.tvtimes), poop));
}
}
});
rootView.findViewById(id.btnaddsleep).setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
sleep = sleep + 0.25;
tvsleep.setText(String.format(getString(string.tvhours), sleep));
}
});
rootView.findViewById(id.btnminussleep).setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
if(sleep > 0)
{
sleep = sleep - 0.25;
tvsleep.setText(String.format(getString(string.tvhours), sleep));
}
}
});
rootView.findViewById(id.btnsave).setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
Toast.makeText(getActivity(), "Report Saved.", Toast.LENGTH_LONG).show();
getActivity().finish();
}
});
return rootView;
}
}
Please advise... Thanks
ViewPager itself has a method setOffscreenPageLimit which allows you to specify number of pages kept by the adapter. So your fragments that are far away will be destroyed.
First of all looking at your code I don't see you doing any memory releasing measures in your fragments onDestroy(). The fact that fragment itself is destroyed and gc'ed does not mean all resources you allocated were removed too.
For example, my big concern is:
imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(childBean.childphoto, childimg, options);
From what I see here it seems that there is a static instance of ImageLoader that gets poked every time a new fragment appears, but I can't see where a dying fragment would ask ImageLoader to unload its stuff. That looks suspicious to me.
If I were you I would dump an HPROF file of my application the moment it took extra 200mb (as you claim) after activity restart and analyze references via MAT (memory analyzer tool). You are clearly having memory leaks issue and I highly doubt the problem is in Fragments themselves not being destroyed.
In case you don't know how to analyze memory heap, here is a good video. I can't count how many times it helped me identifying and getting rid of memory leaks in my apps.
Don't store 'strong' references to ViewPager or ImageView in your Fragment. You're creating a cyclical reference that will keep everything in memory. Instead, if you must keep a reference to ViewPager or any other element that references its context outside of your Activity, try using a WeakReference, e.g:
private WeakReference<ViewPager> mPagerRef;
...
mPagerRef = new WeakReference<ViewPager>(mPager);
...
final ViewPager pager = mPagerRef.get();
if (pager != null) {
pager.setCurrentItem(...);
}
Following this pattern with Objects that store a reference to the Activity or Application context (hint: any ViewGroup, ImageView, Activity, etc.) should prevent "memory leaks" in the form of "retain cycles" from occurring.
it seems that your code is not destroying the view, check this Destroy item from the ViewPager's adapter might solve this issue.
After using the memory analyzer tool in eclipse i found out that what is sticking in my memory is the actual layout of my fragments.
Relative layout in specific.
The reason for this is a CustomTextView that i created that has a custom font set as a typeface.
Typeface face=Typeface.createFromAsset(context.getAssets(), "Helvetica_Neue.ttf");
this.setTypeface(face);
To solve the memory leak i simply did the following answer found here:
public class FontCache {
private static Hashtable<String, Typeface> fontCache = new Hashtable<String, Typeface>();
public static Typeface get(String name, Context context) {
Typeface tf = fontCache.get(name);
if(tf == null) {
try {
tf = Typeface.createFromAsset(context.getAssets(), name);
}
catch (Exception e) {
return null;
}
fontCache.put(name, tf);
}
return tf;
}
}