From bab16ddf275e0ed5eaa3003546a31d0d1d0b1316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=8A=B2=E9=B9=8F?= Date: Tue, 13 Jun 2023 15:11:48 +0800 Subject: [PATCH] Android: implement slider style gallery --- .../pub/doric/shader/slider/SlideAdapter.java | 16 ++- .../shader/slider/SliderLayoutManager.java | 97 +++++++++++++++++++ .../pub/doric/shader/slider/SliderNode.java | 49 ++++++++-- 3 files changed, 154 insertions(+), 8 deletions(-) create mode 100644 doric-android/doric/src/main/java/pub/doric/shader/slider/SliderLayoutManager.java diff --git a/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideAdapter.java b/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideAdapter.java index 88ec4f08..6b140676 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideAdapter.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideAdapter.java @@ -20,16 +20,18 @@ import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + import com.github.pengfeizhou.jscore.JSArray; import com.github.pengfeizhou.jscore.JSDecoder; import com.github.pengfeizhou.jscore.JSNull; import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSValue; -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; import pub.doric.async.AsyncResult; import pub.doric.shader.ViewNode; +import pub.doric.utils.DoricUtils; /** * @Description: pub.doric.shader.slider @@ -46,6 +48,8 @@ class SlideAdapter extends RecyclerView.Adapter { String renderPageFuncId; boolean loop; + float itemWidth; + SlideAdapter(SliderNode sliderNode) { this.sliderNode = sliderNode; } @@ -71,6 +75,10 @@ class SlideAdapter extends RecyclerView.Adapter { } catch (Exception e) { sliderNode.getDoricContext().getDriver().getRegistry().onException(sliderNode.getDoricContext(), e); } + + if (sliderNode.slideStyle.equals("gallery")) { + holder.itemView.getLayoutParams().width = DoricUtils.dp2px(this.itemWidth); + } } @Override @@ -149,6 +157,10 @@ class SlideAdapter extends RecyclerView.Adapter { } } + void setItemWidth(float itemWidth) { + this.itemWidth = itemWidth; + } + static class DoricViewHolder extends RecyclerView.ViewHolder { SlideItemNode slideItemNode; diff --git a/doric-android/doric/src/main/java/pub/doric/shader/slider/SliderLayoutManager.java b/doric-android/doric/src/main/java/pub/doric/shader/slider/SliderLayoutManager.java new file mode 100644 index 00000000..67a5dd1c --- /dev/null +++ b/doric-android/doric/src/main/java/pub/doric/shader/slider/SliderLayoutManager.java @@ -0,0 +1,97 @@ +package pub.doric.shader.slider; + +import android.content.Context; +import android.view.View; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import pub.doric.utils.DoricUtils; + +public class SliderLayoutManager extends LinearLayoutManager { + + private boolean enableGallery; + private float minScale = 1f; + private float itemWidth = 0; + + private float minAlpha = 1f; + + public SliderLayoutManager(Context context, int orientation, boolean reverseLayout) { + super(context, orientation, reverseLayout); + } + + @Override + public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { + if (enableGallery) { + handleHorizontalView(); + } + return super.scrollHorizontallyBy(dx, recycler, state); + } + + @Override + public void onLayoutCompleted(RecyclerView.State state) { + super.onLayoutCompleted(state); + + //scroll won't be called when first layout completed, so need to handle view at first time + if (enableGallery) { + handleView(); + } + } + + public void setEnableGallery(boolean enableGallery) { + this.enableGallery = enableGallery; + } + + public void setItemWidth(float itemWidth) { + this.itemWidth = itemWidth; + } + + public void setMinScale(float minScale) { + this.minScale = minScale; + } + + public void setMinAlpha(float minAlpha) { + this.minAlpha = minAlpha; + } + + private void handleView() { + if (getOrientation() == LinearLayoutManager.HORIZONTAL) { + handleHorizontalView(); + } + } + + private void handleHorizontalView() { + float centerViewLeft = (float) (getWidth() - DoricUtils.dp2px(this.itemWidth)) / 2;//the left when the view is centered + float moveX = DoricUtils.dp2px(this.itemWidth);//movement x from one item to another + calculateScale(centerViewLeft, moveX); + } + + private void calculateScale(float centerViewLeft, float moveDistance) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + assert child != null; + int left = child.getLeft(); + if (getOrientation() == LinearLayoutManager.VERTICAL) { + left = child.getTop(); + } + float factor = (left - centerViewLeft) / moveDistance; + factor = Math.max(-1f, factor); + factor = Math.min(1f, factor); + if (factor > 0) { + // right view to center + scaleAndAlpha(child, 1f - factor * (1 - minScale), 1 - factor * (1 - minAlpha)); + } else { + // left view to center + scaleAndAlpha(child, 1f + factor * (1 - minScale), 1 + factor * (1 - minAlpha)); + } + } + } + + private void scaleAndAlpha(View view, float scale, float alpha) { + view.setPivotX(view.getWidth() / 2f); + view.setPivotY(view.getHeight() / 2f); + view.setScaleX(scale); + view.setScaleY(scale); + view.setAlpha(alpha); + } +} diff --git a/doric-android/doric/src/main/java/pub/doric/shader/slider/SliderNode.java b/doric-android/doric/src/main/java/pub/doric/shader/slider/SliderNode.java index 88716383..e4c1cfe7 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/slider/SliderNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/slider/SliderNode.java @@ -17,6 +17,7 @@ package pub.doric.shader.slider; import android.annotation.SuppressLint; import android.content.Context; +import android.graphics.Rect; import android.text.TextUtils; import android.view.View; @@ -35,6 +36,7 @@ import pub.doric.extension.bridge.DoricPlugin; import pub.doric.extension.bridge.DoricPromise; import pub.doric.shader.SuperNode; import pub.doric.shader.ViewNode; +import pub.doric.utils.DoricUtils; /** * @Description: pub.doric.shader.slider @@ -50,9 +52,10 @@ public class SliderNode extends SuperNode { private boolean loop = false; private String renderPageFuncId; private boolean scrollable = true; - private String slideStyle = null; + String slideStyle = null; private float minScale = 0.618f; private float maxScale = 1; + private float galleryItemWidth = -1f; private int slidePosition; private boolean needSlideToPosition; @@ -64,10 +67,10 @@ public class SliderNode extends SuperNode { @Override protected RecyclerView build() { - RecyclerView recyclerView = new RecyclerView(getContext()); + final RecyclerView recyclerView = new RecyclerView(getContext()); - final LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()) { + final SliderLayoutManager layoutManager = new SliderLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false) { @Override public boolean canScrollHorizontally() { if (!scrollable) { @@ -88,10 +91,28 @@ public class SliderNode extends SuperNode { } }; - layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); recyclerView.setLayoutManager(layoutManager); final PagerSnapHelper snapHelper = new PagerSnapHelper(); snapHelper.attachToRecyclerView(recyclerView); + + recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { + @Override + public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { + if (slideStyle.equals("gallery")) { + int position = parent.getChildAdapterPosition(view); + + LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager(); + assert layoutManager != null; + int count = layoutManager.getItemCount(); + int interval = (recyclerView.getWidth() - DoricUtils.dp2px(SliderNode.this.galleryItemWidth)) / 2; + if (position == 0) { + outRect.left = interval; + } else if (position == count - 1) { + outRect.right = interval; + } + } + } + }); recyclerView.setAdapter(this.slideAdapter); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override @@ -255,8 +276,24 @@ public class SliderNode extends SuperNode { this.slideStyle = prop.asString().value(); } else if (prop.isObject()) { this.slideStyle = prop.asObject().getProperty("type").asString().value(); - this.maxScale = prop.asObject().getProperty("maxScale").asNumber().toFloat(); - this.minScale = prop.asObject().getProperty("minScale").asNumber().toFloat(); + if (this.slideStyle.equals("zoomOut")) { + this.maxScale = prop.asObject().getProperty("maxScale").asNumber().toFloat(); + this.minScale = prop.asObject().getProperty("minScale").asNumber().toFloat(); + } else if (this.slideStyle.equals("gallery")) { + float galleryMinScale = prop.asObject().getProperty("minScale").asNumber().toFloat(); + float galleryMinAlpha = prop.asObject().getProperty("minAlpha").asNumber().toFloat(); + this.galleryItemWidth = prop.asObject().getProperty("itemWidth").asNumber().toFloat(); + + SliderLayoutManager layoutManager = (SliderLayoutManager) mView.getLayoutManager(); + if (layoutManager != null) { + layoutManager.setEnableGallery(true); + layoutManager.setMinScale(galleryMinScale); + layoutManager.setMinAlpha(galleryMinAlpha); + layoutManager.setItemWidth(this.galleryItemWidth); + this.slideAdapter.setItemWidth(this.galleryItemWidth); + } + + } } break; case "slidePosition":