public void setPhotoWidth(int photoWidth) {
mPhotoWidth = photoWidth;
}
public void setPhotoHeight(int photoHeight) {
mPhotoHeight = photoHeight;
}
I use Log.d to check mPhotoWidth and mPhotoHeight inside and after this listener but the value return from getMeasuredWidth() (which is correct) does not assigned to the global variable.
After struggling some days and then writing this post, I realized the answer.
From the log I noticed that “After onGlobalLayout” is logged before “onGlobalLayout” as in the previous post. And if updatePhotoView(); is after ViewTreeObserver.OnGlobalLayoutListener(), the updatePhotoView(); is called early with invalid width and height (zeros in this case) because the global variables are not yet set.
To call updatePhotoView(); at the right time, the method should be inside ViewTreeObserver.OnGlobalLayoutListener().
Here is the complete Code:
CrimeFragment.java
package com.bignerdranch.android.criminalintent;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.FileProvider;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import java.io.File;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import static android.widget.CompoundButton.*;
/**
* Created by behin on 8/7/2017.
*/
public class CrimeFragment extends Fragment {
private static final String ARG_CRIME_ID = "crime_id";
private static final String DIALOG_DATE = "DialogDate";
private static final String DIALOG_PHOTO = "DialogPhoto";
private static final int REQUEST_DATE = 0;
private static final int REQUEST_CONTACT = 1;
private static final int REQUEST_PHOTO = 2;
private Crime mCrime;
private File mPhotoFile;
private EditText mTitleField;
private Button mDateButton;
private CheckBox mSolvedCheckBox;
private Button mSuspectButton;
private Button mReportButton;
private ImageButton mPhotoButton;
private ImageView mPhotoView;
private int mPhotoWidth;
private int mPhotoHeight;
public static CrimeFragment newInstance(UUID crimeId) {
Bundle args = new Bundle();
args.putSerializable(ARG_CRIME_ID, crimeId);
CrimeFragment fragment = new CrimeFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
mPhotoFile = CrimeLab.get(getActivity()).getPhotoFile(mCrime);
}
@Override
public void onPause() {
super.onPause();
CrimeLab.get(getActivity()).updateCrime(mCrime);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, container, false);
mTitleField = (EditText) v.findViewById(R.id.crime_title);
mTitleField.setText(mCrime.getTitle());
mTitleField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
mCrime.setTitle(charSequence.toString());
}
@Override
public void afterTextChanged(Editable editable) {
}
});
mDateButton = (Button) v.findViewById(R.id.crime_date);
updateDate();
mDateButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
FragmentManager manager = getFragmentManager();
DatePickerFragment dialog = DatePickerFragment.newInstance(mCrime.getDate());
dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
dialog.show(manager, DIALOG_DATE);
}
});
mSolvedCheckBox = (CheckBox) v.findViewById(R.id.crime_solved);
mSolvedCheckBox.setChecked(mCrime.isSolved());
mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
mCrime.setSolved(b);
}
});
mReportButton = (Button) v.findViewById(R.id.crime_report);
mReportButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_TEXT, getCrimeReport());
i.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.crime_report_subject));
i = Intent.createChooser(i, getString(R.string.send_report));
startActivity(i);
}
});
final Intent pickContact = new Intent(Intent.ACTION_PICK,
ContactsContract.Contacts.CONTENT_URI);
mSuspectButton = (Button) v.findViewById(R.id.crime_suspect);
mSuspectButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
startActivityForResult(pickContact, REQUEST_CONTACT);
}
});
if (mCrime.getSuspect() != null) {
mSuspectButton.setText(mCrime.getSuspect());
}
final PackageManager packageManager = getActivity().getPackageManager();
if(packageManager.resolveActivity(pickContact, packageManager.MATCH_DEFAULT_ONLY) == null) {
mSuspectButton.setEnabled(false);
}
mPhotoButton = (ImageButton) v.findViewById(R.id.crime_camera);
final Intent captureImage = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
boolean canTakePhoto = mPhotoFile != null &&
captureImage.resolveActivity(packageManager) != null;
mPhotoButton.setEnabled(canTakePhoto);
mPhotoButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = FileProvider.getUriForFile(getActivity(),
"com.bignerdranch.android.criminalintent.fileprovider",
mPhotoFile);
captureImage.putExtra(MediaStore.EXTRA_OUTPUT, uri);
List<ResolveInfo> cameraActivities = getActivity()
.getPackageManager().queryIntentActivities(captureImage,
packageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo activity : cameraActivities) {
getActivity().grantUriPermission(activity.activityInfo.packageName,
uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
startActivityForResult(captureImage, REQUEST_PHOTO);
}
});
mPhotoView = (ImageView) v.findViewById(R.id.crime_photo);
mPhotoView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
FragmentManager manager = getFragmentManager();
PhotoViewerFragment dialog = PhotoViewerFragment.newInstance(mPhotoFile);
dialog.show(manager, DIALOG_PHOTO);
}
});
ViewTreeObserver observer = mPhotoView.getViewTreeObserver();
if (observer.isAlive()) {
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mPhotoView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
mPhotoWidth = mPhotoView.getMeasuredWidth();
mPhotoHeight = mPhotoView.getMeasuredHeight();
updatePhotoView();
}
});
}
return v;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) {
return;
}
if (requestCode == REQUEST_DATE) {
Date date = (Date) data.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
updateDate();
} else if (requestCode == REQUEST_CONTACT && data != null) {
Uri contactUri = data.getData();
// Specify which field you want your query to return values for
String[] queryFields = new String[] { ContactsContract.Contacts.DISPLAY_NAME };
// Perform your query - the contactUri is like a "where" clause here
Cursor c = getActivity().getContentResolver()
.query(contactUri, queryFields, null, null, null);
try {
// Double-check that you actually gor results
if (c.getCount() == 0) {
return;
}
// pull out the first column of the first row of data that is your suspect's name
c.moveToFirst();
String suspect = c.getString(0);
mCrime.setSuspect(suspect);
mSuspectButton.setText(suspect);
} finally {
c.close();
}
} else if (requestCode == REQUEST_PHOTO) {
Uri uri = FileProvider.getUriForFile(getActivity(),
"com.bignerdranch.android.criminalintent.fileprovider",
mPhotoFile);
getActivity().revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
updatePhotoView();
}
}
private void updateDate() {
mDateButton.setText(mCrime.getDate().toString());
}
private String getCrimeReport() {
String solvedString;
if(mCrime.isSolved()) {
solvedString = getString(R.string.crime_report_solved);
} else {
solvedString = getString(R.string.crime_report_unsolved);
}
String dateFormat = "EEE, MMM dd";
String dateString = DateFormat.format(dateFormat, mCrime.getDate()).toString();
String suspect = mCrime.getSuspect();
if (suspect == null) {
suspect = getString(R.string.crime_report_no_suspect);
} else {
suspect = getString(R.string.crime_report_suspect, suspect);
}
String report = getString(R.string.crime_report,
mCrime.getTitle(), dateString, solvedString, suspect);
return report;
}
private void updatePhotoView() {
if (mPhotoFile == null || !mPhotoFile.exists()) {
mPhotoView.setImageDrawable(null);
} else {
Bitmap bitmap = PictureUtils.getScaledBitmap(mPhotoFile.getPath(), mPhotoWidth, mPhotoHeight);
mPhotoView.setImageBitmap(bitmap);
}
}
}
My code is a little different as I interpreted the question to be that we were to write code that didn’t use the PictureUtils.getScaledBitmap(…). I could get the dimensions of mPhotoView after the layout pass and let the BitMapFactory take care of the rest. So there is no need to scale down manually by using the PictureUtils class any more.
@BC2017, you used the method getMeasuredWidth() to get the width of the Imageview
I was considering just using getWidth(). I know that will return the size in pixels, but we want them in device independent pixels (dp). So which method, if any, returns the values in dp. If none of them, then how do we convert to dp?
I think you are right to use getWidth() instead of getMeasuredWidth(), the answer is here stackoverflow.
Both getWidth() and getMeasuredWidth() will return size in px which I think is what you need.
dp we use it in xml file then at runtime it will be converted to px. To know the reason, see the story here medium.
You can read more info about screen densities here android developers. They provide the equation to convert dp to px and give an example when to use it.