Improve marker list layout with photo markers

This commit is contained in:
Jimmy Shih
2013-10-21 11:28:16 -07:00
parent 0e8de4c91a
commit 2e4f4cc4c2
5 changed files with 136 additions and 117 deletions
+60 -74
View File
@@ -14,92 +14,78 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="match_parent" android:layout_width="match_parent" >
android:minHeight="?android:attr/listPreferredItemHeight" <ImageView
android:paddingBottom="12dp" android:contentDescription="@string/icon_marker"
android:paddingRight="12dp" android:id="@+id/list_item_background"
android:paddingTop="8dp" android:layout_height="match_parent"
android:shrinkColumns="1" android:layout_width="match_parent"
android:stretchColumns="1" android:scaleType="centerCrop" />
style="@style/ListItem" > <TableLayout
<!-- First row --> android:layout_height="wrap_content"
<TableRow> android:layout_width="match_parent"
<!-- First column --> android:minHeight="?android:attr/listPreferredItemHeight"
<ImageView android:paddingBottom="12dp"
android:contentDescription="@string/icon_track" android:paddingRight="12dp"
android:id="@+id/list_item_icon" android:paddingTop="8dp"
android:layout_gravity="center_vertical" android:shrinkColumns="1"
android:layout_height="wrap_content" android:stretchColumns="1"
android:layout_marginLeft="8dp" style="@style/ListItem" >
android:layout_marginRight="8dp" <!-- First row -->
android:layout_width="wrap_content" /> <TableRow>
<!-- Second column --> <!-- First column -->
<LinearLayout <ImageView
android:baselineAligned="false" android:contentDescription="@string/icon_track"
android:layout_height="wrap_content" android:id="@+id/list_item_icon"
android:layout_width="match_parent" > android:layout_gravity="center_vertical"
<LinearLayout
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="3" android:layout_marginLeft="8dp"
android:layout_width="0dp" > android:layout_marginRight="8dp"
<TableLayout android:layout_width="wrap_content" />
android:layout_height="wrap_content" <!-- Second column -->
android:layout_weight="1"
android:layout_width="0dp"
android:shrinkColumns="*"
android:stretchColumns="*" >
<TableRow>
<TextView
android:ellipsize="end"
android:id="@+id/list_item_name"
android:singleLine="true"
style="@style/TextMedium" />
</TableRow>
<TableRow>
<!-- Need to match the style set in ListItemUtils.setListItem -->
<TextView
android:ellipsize="end"
android:id="@+id/list_item_time_distance"
android:singleLine="true"
style="@style/TextSmall" />
</TableRow>
</TableLayout>
<ImageView
android:id="@+id/list_item_photo"
android:adjustViewBounds="true"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_weight="1"
android:layout_width="0dp"
android:scaleType="fitCenter" />
</LinearLayout>
<TableLayout <TableLayout
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_width="match_parent"
android:layout_width="0dp" android:shrinkColumns="0"
android:shrinkColumns="*" android:stretchColumns="0" >
android:stretchColumns="*" >
<TableRow> <TableRow>
<TextView
android:ellipsize="end"
android:id="@+id/list_item_name"
android:layout_gravity="bottom"
android:layout_width="fill_parent"
android:singleLine="true"
style="@style/TextMedium" />
<TextView <TextView
android:id="@+id/list_item_date" android:id="@+id/list_item_date"
style="@style/ListItemDate" /> style="@style/ListItemDate" />
</TableRow> </TableRow>
<TableRow> <TableRow>
<!-- Need to match the style set in ListItemUtils.setListItem -->
<TextView
android:ellipsize="end"
android:id="@+id/list_item_time_distance"
android:layout_gravity="bottom"
android:layout_width="fill_parent"
android:singleLine="true"
style="@style/TextSmall" />
<TextView <TextView
android:id="@+id/list_item_time" android:id="@+id/list_item_time"
style="@style/ListItemDate" /> style="@style/ListItemDate" />
</TableRow> </TableRow>
</TableLayout> </TableLayout>
</LinearLayout> </TableRow>
</TableRow> <!-- Second row -->
<!-- Second row --> <TableRow>
<TableRow> <TextView
<TextView android:ellipsize="end"
android:id="@+id/list_item_description" android:id="@+id/list_item_description"
android:layout_column="1" android:layout_column="1"
android:singleLine="false" android:maxLines="2"
style="@style/TextSmall" /> android:singleLine="false"
</TableRow> style="@style/TextSmall" />
</TableLayout> </TableRow>
</TableLayout>
</FrameLayout>
@@ -101,7 +101,7 @@ public class MarkerDetailFragment extends Fragment {
inflater.inflate(R.menu.marker_detail, menu); inflater.inflate(R.menu.marker_detail, menu);
updateWaypoint(false); updateWaypoint(false);
Track track = myTracksProviderUtils.getTrack(waypoint.getTrackId()); Track track = myTracksProviderUtils.getTrack(waypoint.getTrackId());
boolean isSharedWithMe = track != null ? track.isSharedWithMe() : true; boolean isSharedWithMe = track != null ? track.isSharedWithMe() : true;
@@ -114,7 +114,7 @@ public class MarkerDetailFragment extends Fragment {
long markerId = getArguments().getLong(KEY_MARKER_ID); long markerId = getArguments().getLong(KEY_MARKER_ID);
FragmentActivity fragmentActivity = getActivity(); FragmentActivity fragmentActivity = getActivity();
Intent intent; Intent intent;
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.marker_detail_show_on_map: case R.id.marker_detail_show_on_map:
intent = IntentUtils.newIntent(fragmentActivity, TrackDetailActivity.class) intent = IntentUtils.newIntent(fragmentActivity, TrackDetailActivity.class)
@@ -168,11 +168,8 @@ public class MarkerDetailFragment extends Fragment {
} else { } else {
imageView.setVisibility(View.VISIBLE); imageView.setVisibility(View.VISIBLE);
Display defaultDisplay = getActivity().getWindowManager().getDefaultDisplay(); Display defaultDisplay = getActivity().getWindowManager().getDefaultDisplay();
@SuppressWarnings("deprecation") PhotoUtils.setImageVew(imageView, Uri.parse(photoUrl), defaultDisplay.getWidth(),
int displayWidth = defaultDisplay.getWidth(); defaultDisplay.getHeight(), true);
@SuppressWarnings("deprecation")
int displayHeight = defaultDisplay.getHeight();
PhotoUtils.setImageVew(imageView, Uri.parse(photoUrl), displayWidth, displayHeight);
} }
TextView name = (TextView) getView().findViewById(R.id.marker_detail_waypoint_name); TextView name = (TextView) getView().findViewById(R.id.marker_detail_waypoint_name);
@@ -193,8 +190,8 @@ public class MarkerDetailFragment extends Fragment {
TextView name = (TextView) getView().findViewById(R.id.marker_detail_statistics_name); TextView name = (TextView) getView().findViewById(R.id.marker_detail_statistics_name);
name.setText(getString(R.string.generic_name_line, waypoint.getName())); name.setText(getString(R.string.generic_name_line, waypoint.getName()));
StatsUtils.setTripStatisticsValues(getActivity(), waypoint.getTripStatistics(), StatsUtils.setTripStatisticsValues(
PreferencesUtils.RECORDING_TRACK_ID_DEFAULT); getActivity(), waypoint.getTripStatistics(), PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
StatsUtils.setLocationValues(getActivity(), waypoint.getLocation(), false); StatsUtils.setLocationValues(getActivity(), waypoint.getLocation(), false);
} }
} }
@@ -40,16 +40,19 @@ public class BitmapLoader extends AsyncTask<Void, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference; private final WeakReference<ImageView> imageViewReference;
private final Uri uri; private final Uri uri;
private final int displayWidth; private final int targetWidth;
private final int displayHeight; private final int targetHeight;
private final boolean fitWithin;
public BitmapLoader(ImageView imageView, Uri uri, int displayWidth, int displayHeight) { public BitmapLoader(
ImageView imageView, Uri uri, int targetWidth, int targetHeight, boolean fitWithin) {
// Use a WeakReference to ensure the ImageView can be garbage collected // Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView); imageViewReference = new WeakReference<ImageView>(imageView);
this.uri = uri; this.uri = uri;
this.displayWidth = displayWidth; this.targetWidth = targetWidth;
this.displayHeight = displayHeight; this.targetHeight = targetHeight;
this.fitWithin = fitWithin;
} }
public Uri getUri() { public Uri getUri() {
@@ -65,13 +68,6 @@ public class BitmapLoader extends AsyncTask<Void, Void, Bitmap> {
options.inJustDecodeBounds = true; options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(uri.getPath(), options); BitmapFactory.decodeFile(uri.getPath(), options);
// Set targetWidth and targetHeight
int targetWidth = displayWidth;
int targetHeight = displayHeight;
if (targetHeight == 0) {
targetHeight = (int) (targetWidth * ((float) options.outHeight / (float) options.outWidth));
}
// Set imageWidth and imageHeight based on image rotation // Set imageWidth and imageHeight based on image rotation
int rotation = getRotation(); int rotation = getRotation();
int imageWidth; int imageWidth;
@@ -87,20 +83,26 @@ public class BitmapLoader extends AsyncTask<Void, Void, Bitmap> {
// Get a scaled down version of the image // Get a scaled down version of the image
options.inJustDecodeBounds = false; options.inJustDecodeBounds = false;
options.inSampleSize = getInSampleSize(imageWidth, imageHeight, targetWidth, targetHeight); options.inSampleSize = getInSampleSize(imageWidth, imageHeight);
options.inPurgeable = true; options.inPurgeable = true;
Bitmap scaledBitmap = BitmapFactory.decodeFile(uri.getPath(), options); Bitmap scaledBitmap = BitmapFactory.decodeFile(uri.getPath(), options);
// Get the final bitmap after rotating the scaled down image // Get the final bitmap after rotating the scaled down image
Bitmap bitmap; Bitmap bitmap;
if (rotation == 0) { if (rotation == 0 && fitWithin) {
bitmap = scaledBitmap; bitmap = scaledBitmap;
} else { } else {
Matrix matrix = new Matrix(); Matrix matrix = new Matrix();
matrix.postRotate(rotation); matrix.postRotate(rotation);
int offset = 0;
int height = scaledBitmap.getHeight();
if (!fitWithin && height > targetHeight) {
offset = (height - targetHeight) / 2;
height = targetHeight;
}
bitmap = Bitmap.createBitmap( bitmap = Bitmap.createBitmap(
scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true); scaledBitmap, 0, offset, scaledBitmap.getWidth(), height, matrix, true);
scaledBitmap.recycle(); scaledBitmap.recycle();
} }
return bitmap; return bitmap;
@@ -148,10 +150,8 @@ public class BitmapLoader extends AsyncTask<Void, Void, Bitmap> {
* *
* @param imageWidth the image width * @param imageWidth the image width
* @param imageHeight the image height * @param imageHeight the image height
* @param targetWidth the target width
* @param targetHeight the target height
*/ */
private int getInSampleSize(int imageWidth, int imageHeight, int targetWidth, int targetHeight) { private int getInSampleSize(int imageWidth, int imageHeight) {
float widthRatio = 1; float widthRatio = 1;
if (imageWidth > targetWidth) { if (imageWidth > targetWidth) {
widthRatio = (float) imageWidth / (float) targetWidth; widthRatio = (float) imageWidth / (float) targetWidth;
@@ -162,11 +162,21 @@ public class BitmapLoader extends AsyncTask<Void, Void, Bitmap> {
heightRatio = (float) imageHeight / (float) targetHeight; heightRatio = (float) imageHeight / (float) targetHeight;
} }
/* double size;
* To fit within the target area, return the larger sample ratio so the if (fitWithin) {
* image will not be larger than the target dimensions. Use Math.floor to /*
* not under sample. * To fit within the target area, return the larger sample ratio so the
*/ * image will not be larger than the target dimensions.
return (int) Math.floor(Math.max(widthRatio, heightRatio)); */
size = Math.max(widthRatio, heightRatio);
} else {
/*
* To fill the target area, return the smaller ratio so the image will
* cover both dimensions.
*/
size = Math.min(widthRatio, heightRatio);
}
// Use Math.floor to not under-sample.
return (int) Math.floor(size);
} }
} }
@@ -20,6 +20,7 @@ import com.google.android.maps.mytracks.R;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray;
import android.net.Uri; import android.net.Uri;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.view.Display; import android.view.Display;
@@ -36,6 +37,8 @@ import java.util.Calendar;
*/ */
public class ListItemUtils { public class ListItemUtils {
private static final int LIST_PREFERRED_ITEM_HEIGHT_DEFAULT = 128;
private ListItemUtils() {} private ListItemUtils() {}
/** /**
@@ -87,17 +90,14 @@ public class ListItemUtils {
setTextView(timeDistanceTextView, setTextView(timeDistanceTextView,
getTimeDistance(activity, isRecording, isPaused, sharedOwner, totalTime, totalDistance)); getTimeDistance(activity, isRecording, isPaused, sharedOwner, totalTime, totalDistance));
// Set photoUrl ImageView photo = (ImageView) view.findViewById(R.id.list_item_background);
ImageView photo = (ImageView) view.findViewById(R.id.list_item_photo);
if (photoUrl == null || photoUrl.equals("")) { if (photoUrl == null || photoUrl.equals("")) {
photo.setVisibility(View.GONE); photo.setVisibility(View.GONE);
} else { } else {
photo.setVisibility(View.VISIBLE); photo.setVisibility(View.VISIBLE);
Display defaultDisplay = activity.getWindowManager().getDefaultDisplay(); Display defaultDisplay = activity.getWindowManager().getDefaultDisplay();
// Set the initial width to 33% of the display width. PhotoUtils.setImageVew(
int width = (int) (defaultDisplay.getWidth() * .33); photo, Uri.parse(photoUrl), defaultDisplay.getWidth(), getPhotoHeight(activity), false);
int height = 0;
PhotoUtils.setImageVew(photo, Uri.parse(photoUrl), width, height);
} }
// Set date/time // Set date/time
@@ -108,6 +108,15 @@ public class ListItemUtils {
TextView timeTextView = (TextView) view.findViewById(R.id.list_item_time); TextView timeTextView = (TextView) view.findViewById(R.id.list_item_time);
setTextView(timeTextView, startTimeDisplay[1]); setTextView(timeTextView, startTimeDisplay[1]);
/*
* If column0 is GONE, change to INVISIBLE so column1 is placed at the
* correct position.
*/
if (timeTextView.getVisibility() == View.VISIBLE
&& timeDistanceTextView.getVisibility() == View.GONE) {
timeDistanceTextView.setVisibility(View.INVISIBLE);
}
// Set category/description // Set category/description
TextView descriptionTextView = (TextView) view.findViewById(R.id.list_item_description); TextView descriptionTextView = (TextView) view.findViewById(R.id.list_item_description);
setTextView(descriptionTextView, setTextView(descriptionTextView,
@@ -201,4 +210,17 @@ public class ListItemUtils {
textView.setText(value); textView.setText(value);
} }
} }
/**
* Gets the photo height.
*
* @param context the context
*/
private static int getPhotoHeight(Context context) {
int[] attrs = new int[] { android.R.attr.listPreferredItemHeight };
TypedArray typeArray = context.obtainStyledAttributes(attrs);
int height = typeArray.getDimensionPixelSize(0, LIST_PREFERRED_ITEM_HEIGHT_DEFAULT);
typeArray.recycle();
return 2 * height;
}
} }
@@ -37,13 +37,17 @@ public class PhotoUtils {
* *
* @param imageView the image view * @param imageView the image view
* @param uri the image uri * @param uri the image uri
* @param displayWidth the display width * @param targetWidth the target width
* @param displayHeight the display height * @param targetHeight the target height
* @param fitWithin true to fit within the target area in order to display the
* entire image (no cropping). False to fill the entire target area.
* (allow cropping).
*/ */
public static void setImageVew( public static void setImageVew(
ImageView imageView, Uri uri, int displayWidth, int displayHeight) { ImageView imageView, Uri uri, int targetWidth, int targetHeight, boolean fitWithin) {
if (cancelBitmapLoader(imageView, uri)) { if (cancelBitmapLoader(imageView, uri)) {
BitmapLoader bitmapLoader = new BitmapLoader(imageView, uri, displayWidth, displayHeight); BitmapLoader bitmapLoader = new BitmapLoader(
imageView, uri, targetWidth, targetHeight, fitWithin);
WeakReference<BitmapLoader> bitmapLoaderReference = new WeakReference<BitmapLoader>( WeakReference<BitmapLoader> bitmapLoaderReference = new WeakReference<BitmapLoader>(
bitmapLoader); bitmapLoader);
imageView.setTag(bitmapLoaderReference); imageView.setTag(bitmapLoaderReference);