[android] place page hotel gallery

This commit is contained in:
Roman Romanov 2016-09-14 12:40:37 +04:00
parent 99ae639ca4
commit 10a5e9620d
15 changed files with 524 additions and 13 deletions

View file

@ -57,6 +57,8 @@ dependencies {
// TODO remove this library when default LinearLayoutManager will be fixed.
compile 'org.solovyev.android.views:linear-layout-manager:0.5@aar'
compile 'com.timehop.stickyheadersrecyclerview:library:0.4.3@aar'
//Glide
compile 'com.github.bumptech.glide:glide:3.7.0'
}
def getDate() {

View file

@ -14,6 +14,7 @@ jmethodID g_hotelClassCtor;
jmethodID g_priceCallback;
jmethodID g_descriptionCallback;
jmethodID g_facilitiesCallback;
jmethodID g_imagesCallback;
void PrepareClassRefs(JNIEnv * env, jclass hotelClass)
{
@ -30,6 +31,8 @@ void PrepareClassRefs(JNIEnv * env, jclass hotelClass)
g_descriptionCallback = jni::GetStaticMethodID(env, g_hotelClass, "onDescriptionReceived", "(Ljava/lang/String;Ljava/lang/String;)V");
// static void onFacilitiesReceived(final String id, int[] ids, String[] names)
g_facilitiesCallback = jni::GetStaticMethodID(env, g_hotelClass, "onFacilitiesReceived", "(Ljava/lang/String;[I[Ljava/lang/String;)V");
// static void onImagesReceived(final String id, String[] urls)
g_imagesCallback = jni::GetStaticMethodID(env, g_hotelClass, "onImagesReceived", "(Ljava/lang/String;[Ljava/lang/String;)V");
}
} // namespace
@ -108,4 +111,23 @@ Java_com_mapswithme_maps_widget_placepage_SponsoredHotel_nativeRequestFacilities
jni::ToJavaStringArray(env, {"Bar", "Terrace", "Fitness Center", "Pets are allowed on request", "Restaurant", "Private parking", "Ghost Busters"}));
}
// static void nativeRequestImages(String id, String locale);
JNIEXPORT void JNICALL
Java_com_mapswithme_maps_widget_placepage_SponsoredHotel_nativeRequestImages(JNIEnv * env, jclass clazz, jstring id, jstring locale)
{
PrepareClassRefs(env, clazz);
string const hotelId = jni::ToNativeString(env, id);
string const localeCode = jni::ToNativeString(env, locale);
//TODO make request
env->CallStaticVoidMethod(g_hotelClass, g_imagesCallback, jni::ToJavaString(env, hotelId),
jni::ToJavaStringArray(env, {"http://www.libertyhotelslara.com/dosyalar/resimler/liberty-lara-hotel1.jpg",
"https://www.omnihotels.com/-/media/images/hotels/ausctr/pool/ausctr-omni-austin-hotel-downtown-evening-pool.jpg?h=660&la=en&w=1170",
"http://www.thefloridahotelorlando.com/var/floridahotelorlando/storage/images/media/images/photo-gallery/hotel-images/florida-hotel-orlando-night/27177-1-eng-US/Florida-Hotel-Orlando-Night.jpg",
"http://www.college-hotel.com/client/cache/contenu/_500____college-hotelp1diapo1_718.jpg",
"http://top10hotelbookingsites.webs.com/besthotelsites-1.jpg",
"http://www.litorehotel.com/web/en/images/placeholders/1920x1200-0.jpg"}));
}
} // extern "C"

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size android:width="@dimen/margin_quarter"
android:height="@dimen/margin_quarter"/>
<solid android:color="@android:color/transparent"/>
</shape>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFF"
android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector>

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="@dimen/placepage_hotel_gallery_width"
android:layout_height="@dimen/placepage_hotel_gallery_height"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground">
<ImageView
android:id="@+id/iv__image"
android:layout_width="@dimen/placepage_hotel_gallery_width"
android:layout_height="@dimen/placepage_hotel_gallery_height"
tools:src="@color/base_green"/>
<TextView
android:id="@+id/tv__more"
style="@style/PlacePageGalleryText"
android:text="@string/placepage_more_button"
android:visibility="gone"
tools:visibility="visible"/>
</FrameLayout>

View file

@ -27,14 +27,12 @@
android:layout_marginBottom="@dimen/margin_half"
tools:visibility="gone"/>
<include layout="@layout/place_page_hotel_gallery"/>
<include layout="@layout/place_page_hotel_facilities"/>
<include layout="@layout/divider_horizontal"/>
<include layout="@layout/place_page_hotel_description"/>
<include layout="@layout/divider_horizontal"/>
<include layout="@layout/place_page_placename"/>
<include layout="@layout/place_page_opening_hours"/>

View file

@ -2,12 +2,18 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll__place_hotel_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
style="@style/PlacePageItemFrame"
android:minHeight="@dimen/height_block_base"
android:visibility="gone"
tools:background="#20FF0000"
tools:visibility="visible">
<TextView
android:layout_marginLeft="@dimen/margin_base"
android:layout_marginRight="@dimen/margin_base"
android:layout_marginTop="@dimen/margin_base"
style="@style/PlacePageTitleText"
android:text="@string/details"/>
@ -15,7 +21,10 @@
android:id="@+id/tv__place_hotel_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_base"
android:layout_marginRight="@dimen/margin_base"
android:layout_marginTop="@dimen/margin_half_plus"
android:layout_marginBottom="@dimen/margin_half_plus"
android:textAppearance="@style/MwmTextAppearance.Body3.Primary"
android:maxLines="5"
tools:text="One of our top picks in New York City. This boutique hotel in the Manhattan neighborhood of Nolita features a private rooftop and rooms with free WiFi. The Bowery subway station is 1 block from this New York hotel."/>
@ -23,8 +32,12 @@
<TextView
android:id="@+id/tv__place_hotel_more"
style="@style/PlacePageMetadataText.Button"
android:layout_marginLeft="@dimen/margin_base"
android:layout_marginRight="@dimen/margin_base"
android:height="@dimen/height_block_base"
android:background="?clickableBackground"
android:gravity="center"
android:text="@string/placepage_more_button"/>
<include layout="@layout/divider_horizontal"/>
</LinearLayout>

View file

@ -2,12 +2,18 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll__place_hotel_facilities"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
style="@style/PlacePageItemFrame"
android:minHeight="@dimen/height_block_base"
android:visibility="gone"
tools:background="#4000FFFF"
tools:visibility="visible">
<TextView
android:layout_marginLeft="@dimen/margin_base"
android:layout_marginRight="@dimen/margin_base"
android:layout_marginTop="@dimen/margin_base"
style="@style/PlacePageTitleText"
android:text="@string/placepage_hotel_facilities"/>
@ -15,15 +21,22 @@
android:id="@+id/gv__place_hotel_facilities"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_base"
android:layout_marginRight="@dimen/margin_base"
android:layout_marginTop="@dimen/margin_base"
android:layout_marginBottom="@dimen/margin_base"
android:numColumns="2"
tools:listitem="@layout/item_facility"/>
<TextView
android:id="@+id/tv__place_hotel_facilities_more"
style="@style/PlacePageMetadataText.Button"
android:layout_marginLeft="@dimen/margin_base"
android:layout_marginRight="@dimen/margin_base"
android:height="@dimen/height_block_base"
android:background="?clickableBackground"
android:gravity="center"
android:text="@string/placepage_more_button"/>
<include layout="@layout/divider_horizontal"/>
</LinearLayout>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll__place_hotel_gallery"
android:layout_width="match_parent"
android:layout_height="@dimen/placepage_hotel_gallery_height"
android:orientation="horizontal"
android:minHeight="@dimen/placepage_hotel_gallery_height"
tools:background="#20FF0000"
tools:visibility="visible">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv__place_hotel_gallery"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_gallery"/>
</LinearLayout>

View file

@ -144,6 +144,8 @@
<dimen name="altitude_chart_image_height">40dp</dimen>
<dimen name="altitude_chart_image_width">232dp</dimen>
<!-- Gallery-->
<dimen name="placepage_hotel_gallery_height">100dp</dimen>
<dimen name="placepage_hotel_gallery_width">150dp</dimen>
</resources>

View file

@ -65,6 +65,17 @@
<item name="android:textAppearance">@style/MwmTextAppearance.PlacePage.Title</item>
</style>
<style name="PlacePageGalleryText">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_gravity">center</item>
<item name="android:gravity">center</item>
<item name="android:textColor">@color/white_primary</item>
<item name="android:textAllCaps">true</item>
<item name="android:drawableRight">@drawable/ic_chevron_right_white</item>
<item name="android:drawableEnd" tools:targetApi="jelly_bean_mr1">@drawable/ic_chevron_right_white</item>
</style>
<style name="PlacePageMetadataIcon">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>

View file

@ -0,0 +1,169 @@
package com.mapswithme.maps.widget.placepage;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import com.mapswithme.maps.R;
import com.mapswithme.maps.widget.recycler.RecyclerClickListener;
import com.mapswithme.util.UiUtils;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.List;
public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.ViewHolder> {
private static final int MAX_COUNT = 5;
private final Context mContext;
private List<SponsoredHotel.Image> mItems = new ArrayList<>();
private final List<Item> mLoadedItems = new ArrayList<>();
private RecyclerClickListener mListener;
private final int mImageWidth;
private final int mImageHeight;
private final Object mMutex = new Object();
private final List<DownloadState> mDownloadStates = new ArrayList<>();
public GalleryAdapter(Context context) {
mContext = context;
mImageWidth = (int)context.getResources().getDimension(R.dimen.placepage_hotel_gallery_width);
mImageHeight = (int)context.getResources().getDimension(R.dimen.placepage_hotel_gallery_height);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(mContext)
.inflate(R.layout.item_gallery, parent, false), mListener);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Item item = mLoadedItems.get(position);
item.setShowMore(position == MAX_COUNT - 1);
holder.bind(item, position);
}
@Override
public int getItemCount() {
synchronized (mMutex) {
return mLoadedItems.size();
}
}
public void setItems(List<SponsoredHotel.Image> items) {
mItems = items;
synchronized (mMutex) {
mLoadedItems.clear();
for (DownloadState state: mDownloadStates) {
state.isCanceled = true;
}
mDownloadStates.clear();
}
loadImages();
notifyDataSetChanged();
}
public void setListener(RecyclerClickListener listener) {
mListener = listener;
}
private void loadImages() {
int size = mItems.size();
if (size > MAX_COUNT) {
size = MAX_COUNT;
}
for (int i = 0; i < size; i++) {
final DownloadState state;
synchronized (mMutex) {
state = new DownloadState();
mDownloadStates.add(state);
}
SponsoredHotel.Image image = mItems.get(i);
Glide.with(mContext)
.load(image.getUrl())
.asBitmap()
.centerCrop()
.into(new SimpleTarget<Bitmap>(mImageWidth, mImageHeight) {
@Override
public void onResourceReady(Bitmap resource,
GlideAnimation<? super Bitmap> glideAnimation) {
synchronized (mMutex) {
if (state.isCanceled) {
return;
}
int size = mLoadedItems.size();
mLoadedItems.add(new Item(resource));
notifyItemInserted(size);
}
}
});
}
}
static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView mImage;
private View mMore;
private final RecyclerClickListener mListener;
private int mPosition;
public ViewHolder(View itemView, RecyclerClickListener listener) {
super(itemView);
mListener = listener;
mImage = (ImageView) itemView.findViewById(R.id.iv__image);
mMore = itemView.findViewById(R.id.tv__more);
itemView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (mListener == null) {
return;
}
mListener.onItemClick(v, mPosition);
}
public void bind(Item item, int position) {
mPosition = position;
mImage.setImageBitmap(item.getBitmap());
if (item.isShowMore()) {
UiUtils.show(mMore);
} else {
UiUtils.hide(mMore);
}
}
}
static class Item {
private final Bitmap mBitmap;
private boolean isShowMore;
Item(Bitmap bitmap) {
this.mBitmap = bitmap;
}
public Bitmap getBitmap() {
return mBitmap;
}
public void setShowMore(boolean showMore) {
isShowMore = showMore;
}
public boolean isShowMore() {
return isShowMore;
}
}
static class DownloadState {
boolean isCanceled = false;
}
}

View file

@ -12,6 +12,9 @@ import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@ -65,6 +68,8 @@ import com.mapswithme.maps.widget.BaseShadowController;
import com.mapswithme.maps.widget.LineCountTextView;
import com.mapswithme.maps.widget.ObservableScrollView;
import com.mapswithme.maps.widget.ScrollViewShadowController;
import com.mapswithme.maps.widget.recycler.DividerItemDecoration;
import com.mapswithme.maps.widget.recycler.RecyclerClickListener;
import com.mapswithme.util.Graphics;
import com.mapswithme.util.StringUtils;
import com.mapswithme.util.ThemeUtils;
@ -82,7 +87,9 @@ public class PlacePageView extends RelativeLayout
SponsoredHotel.OnPriceReceivedListener,
SponsoredHotel.OnDescriptionReceivedListener,
LineCountTextView.OnLineCountCalculatedListener,
SponsoredHotel.OnFacilitiesReceivedListener {
SponsoredHotel.OnFacilitiesReceivedListener,
SponsoredHotel.OnImagesReceivedListener,
RecyclerClickListener {
private static final String PREF_USE_DMS = "use_dms";
private boolean mIsDocked;
@ -135,8 +142,9 @@ public class PlacePageView extends RelativeLayout
private LineCountTextView mTvHotelDescription;
private View mHotelMoreDescription;
private View mHotelFacilities;
private GridView mGvHotelFacilities;
private View mHotelMoreFacilities;
private View mHotelGallery;
private RecyclerView mRvHotelGallery;
// Animations
private BaseShadowController mShadowController;
@ -148,6 +156,7 @@ public class PlacePageView extends RelativeLayout
private String mSponsoredHotelPrice;
private boolean mIsLatLonDms;
private FacilitiesAdapter mFacilitiesAdapter = new FacilitiesAdapter();
private GalleryAdapter mGalleryAdapter;
// Downloader`s stuff
private DownloaderStatusIcon mDownloaderIcon;
@ -292,10 +301,20 @@ public class PlacePageView extends RelativeLayout
mTvHotelDescription.setListener(this);
mHotelMoreDescription.setOnClickListener(this);
mHotelFacilities = findViewById(R.id.ll__place_hotel_facilities);
mGvHotelFacilities = (GridView) findViewById(R.id.gv__place_hotel_facilities);
GridView gvHotelFacilities = (GridView) findViewById(R.id.gv__place_hotel_facilities);
mHotelMoreFacilities = findViewById(R.id.tv__place_hotel_facilities_more);
mGvHotelFacilities.setAdapter(mFacilitiesAdapter);
gvHotelFacilities.setAdapter(mFacilitiesAdapter);
mHotelMoreFacilities.setOnClickListener(this);
mHotelGallery = findViewById(R.id.ll__place_hotel_gallery);
mRvHotelGallery = (RecyclerView) findViewById(
R.id.rv__place_hotel_gallery);
mRvHotelGallery.setLayoutManager(new LinearLayoutManager(getContext(),
LinearLayoutManager.HORIZONTAL, false));
mRvHotelGallery.addItemDecoration(new DividerItemDecoration(ContextCompat.getDrawable(getContext(),
R.drawable.divider_transparent)));
mGalleryAdapter = new GalleryAdapter(getContext());
mGalleryAdapter.setListener(this);
mRvHotelGallery.setAdapter(mGalleryAdapter);
mButtons = new PlacePageButtons(this, ppButtons, new PlacePageButtons.ItemListener()
{
@ -427,6 +446,7 @@ public class PlacePageView extends RelativeLayout
SponsoredHotel.setPriceListener(this);
SponsoredHotel.setDescriptionListener(this);
SponsoredHotel.setFacilitiesListener(this);
SponsoredHotel.setImagesListener(this);
}
@Override
@ -476,11 +496,31 @@ public class PlacePageView extends RelativeLayout
mHotelMoreFacilities.setVisibility(facilities.size() > 6 ? VISIBLE : GONE);
}
@Override
public void onImagesReceived(String id, List<SponsoredHotel.Image> images) {
if (mSponsoredHotel == null || !TextUtils.equals(id, mSponsoredHotel.getId())) {
return;
}
if (images == null || images.isEmpty()) {
UiUtils.hide(mHotelGallery);
return;
}
UiUtils.show(mHotelGallery);
mGalleryAdapter.setItems(images);
mRvHotelGallery.scrollToPosition(0);
}
@Override
public void onLineCountCalculated(boolean grater) {
mHotelMoreDescription.setVisibility(grater ? VISIBLE : GONE);
}
@Override
public void onItemClick(View v, int position) {
// TODO call gallery activity
}
private void onBookingClick(final boolean book)
{
// TODO (trashkalmar): Set correct text
@ -619,6 +659,7 @@ public class PlacePageView extends RelativeLayout
SponsoredHotel.requestPrice(mSponsoredHotel.getId(), currency.getCurrencyCode());
SponsoredHotel.requestDescription(mSponsoredHotel.getId(), locale.toString());
SponsoredHotel.requestFacilities(mSponsoredHotel.getId(), locale.toString());
SponsoredHotel.requestImages(mSponsoredHotel.getId(), locale.toString());
}
String country = MapManager.nativeGetSelectedCountry();
@ -720,6 +761,7 @@ public class PlacePageView extends RelativeLayout
refreshMetadataOrHide(TextUtils.isEmpty(website) ? mMapObject.getMetadata(Metadata.MetadataType.FMD_URL) : website, mWebsite, mTvWebsite);
UiUtils.hide(mHotelDescription);
UiUtils.hide(mHotelFacilities);
UiUtils.hide(mHotelGallery);
}
else {
UiUtils.hide(mWebsite);

View file

@ -50,6 +50,18 @@ public final class SponsoredHotel
}
}
public static class Image {
private final String url;
public Image(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
}
interface OnPriceReceivedListener
{
void onPriceReceived(String id, String price, String currency);
@ -65,15 +77,23 @@ public final class SponsoredHotel
void onFacilitiesReceived(String id, List<FacilityType> facilities);
}
interface OnImagesReceivedListener
{
void onImagesReceived(String id, List<Image> images);
}
// Hotel ID -> Price
private static final Map<String, Price> sPriceCache = new HashMap<>();
// Hotel ID -> Description
private static final Map<String, String> sDescriptionCache = new HashMap<>();
// Hotel ID -> Facilities
private static final Map<String, List<FacilityType>> sFacilitiesCache = new HashMap<>();
// Hotel ID -> Images
private static final Map<String, List<Image>> sImagesCache = new HashMap<>();
private static WeakReference<OnPriceReceivedListener> sPriceListener;
private static WeakReference<OnDescriptionReceivedListener> sDescriptionListener;
private static WeakReference<OnFacilitiesReceivedListener> sFacilityListener;
private static WeakReference<OnImagesReceivedListener> sImagesListener;
private String mId;
@ -135,6 +155,11 @@ public final class SponsoredHotel
sFacilityListener = new WeakReference<>(listener);
}
public static void setImagesListener(OnImagesReceivedListener listener)
{
sImagesListener = new WeakReference<>(listener);
}
@DrawableRes
public static int mapFacilityId(int facilityId) {
// TODO map facility id to drawable resource
@ -165,7 +190,7 @@ public final class SponsoredHotel
if (facilities != null) {
OnFacilitiesReceivedListener listener = sFacilityListener.get();
if (listener == null)
sDescriptionListener = null;
sFacilityListener = null;
else
listener.onFacilitiesReceived(id, facilities);
}
@ -173,6 +198,20 @@ public final class SponsoredHotel
nativeRequestFacilities(id, locale);
}
static void requestImages(String id, String locale)
{
List<Image> images = sImagesCache.get(id);
if (images != null) {
OnImagesReceivedListener listener = sImagesListener.get();
if (listener == null)
sImagesListener = null;
else
listener.onImagesReceived(id, images);
}
nativeRequestImages(id, locale);
}
@SuppressWarnings("unused")
private static void onPriceReceived(String id, String price, String currency)
{
@ -218,14 +257,35 @@ public final class SponsoredHotel
OnFacilitiesReceivedListener listener = sFacilityListener.get();
if (listener == null)
sDescriptionListener = null;
sFacilityListener = null;
else
listener.onFacilitiesReceived(id, result);
}
@SuppressWarnings("unused")
private static void onImagesReceived(String id, String[] urls)
{
if (urls.length == 0)
return;
List<Image> result = new ArrayList<>();
for (int i = 0; i < urls.length; i++) {
result.add(new Image(urls[i]));
}
sImagesCache.put(id, result);
OnImagesReceivedListener listener = sImagesListener.get();
if (listener == null)
sImagesListener = null;
else
listener.onImagesReceived(id, result);
}
@Nullable
public static native SponsoredHotel nativeGetCurrent();
private static native void nativeRequestPrice(String id, String currencyCode);
private static native void nativeRequestDescription(String id, String locale);
private static native void nativeRequestFacilities(String id, String locale);
private static native void nativeRequestImages(String id, String locale);
}

View file

@ -0,0 +1,124 @@
package com.mapswithme.maps.widget.recycler;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* Adds interior dividers to a RecyclerView with a LinearLayoutManager or its
* subclass.
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private Drawable mDivider;
private int mOrientation;
/**
* Sole constructor. Takes in a {@link Drawable} to be used as the interior
* divider.
*
* @param divider A divider {@code Drawable} to be drawn on the RecyclerView
*/
public DividerItemDecoration(Drawable divider) {
mDivider = divider;
}
/**
* Draws horizontal or vertical dividers onto the parent RecyclerView.
*
* @param canvas The {@link Canvas} onto which dividers will be drawn
* @param parent The RecyclerView onto which dividers are being added
* @param state The current RecyclerView.State of the RecyclerView
*/
@Override
public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == LinearLayoutManager.HORIZONTAL) {
drawHorizontalDividers(canvas, parent);
} else if (mOrientation == LinearLayoutManager.VERTICAL) {
drawVerticalDividers(canvas, parent);
}
}
/**
* Determines the size and location of offsets between items in the parent
* RecyclerView.
*
* @param outRect The {@link Rect} of offsets to be added around the child
* view
* @param view The child view to be decorated with an offset
* @param parent The RecyclerView onto which dividers are being added
* @param state The current RecyclerView.State of the RecyclerView
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (parent.getChildAdapterPosition(view) == 0) {
return;
}
mOrientation = ((LinearLayoutManager) parent.getLayoutManager()).getOrientation();
if (mOrientation == LinearLayoutManager.HORIZONTAL) {
outRect.left = mDivider.getIntrinsicWidth();
} else if (mOrientation == LinearLayoutManager.VERTICAL) {
outRect.top = mDivider.getIntrinsicHeight();
}
}
/**
* Adds dividers to a RecyclerView with a LinearLayoutManager or its
* subclass oriented horizontally.
*
* @param canvas The {@link Canvas} onto which horizontal dividers will be
* drawn
* @param parent The RecyclerView onto which horizontal dividers are being
* added
*/
private void drawHorizontalDividers(Canvas canvas, RecyclerView parent) {
int parentTop = parent.getPaddingTop();
int parentBottom = parent.getHeight() - parent.getPaddingBottom();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount - 1; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int parentLeft = child.getRight() + params.rightMargin;
int parentRight = parentLeft + mDivider.getIntrinsicWidth();
mDivider.setBounds(parentLeft, parentTop, parentRight, parentBottom);
mDivider.draw(canvas);
}
}
/**
* Adds dividers to a RecyclerView with a LinearLayoutManager or its
* subclass oriented vertically.
*
* @param canvas The {@link Canvas} onto which vertical dividers will be
* drawn
* @param parent The RecyclerView onto which vertical dividers are being
* added
*/
private void drawVerticalDividers(Canvas canvas, RecyclerView parent) {
int parentLeft = parent.getPaddingLeft();
int parentRight = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount - 1; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int parentTop = child.getBottom() + params.bottomMargin;
int parentBottom = parentTop + mDivider.getIntrinsicHeight();
mDivider.setBounds(parentLeft, parentTop, parentRight, parentBottom);
mDivider.draw(canvas);
}
}
}