From beec5d87765f211f565dd9238fc003283d825db3 Mon Sep 17 00:00:00 2001 From: seven332 Date: Tue, 21 Apr 2015 18:58:52 +0800 Subject: [PATCH] Add SearchBar list animation --- app/app.iml | 1 - app/build.gradle | 1 - app/proguard-rules.pro | 6 +- .../android/support/v7/widget/CardView.java | 401 ++++++++++++++++++ .../support/v7/widget/CardViewApi21.java | 111 +++++ .../support/v7/widget/CardViewDelegate.java | 31 ++ .../support/v7/widget/CardViewEclairMr1.java | 161 +++++++ .../support/v7/widget/CardViewImpl.java | 49 +++ .../v7/widget/CardViewJellybeanMr1.java | 35 ++ .../support/v7/widget/RoundRectDrawable.java | 132 ++++++ .../widget/RoundRectDrawableWithShadow.java | 354 ++++++++++++++++ .../com/hippo/ehviewer/widget/SearchBar.java | 106 +++++ .../main/res/layout/scene_gallery_list.xml | 14 +- app/src/main/res/layout/widget_search_bar.xml | 6 +- app/src/main/res/values/cardview_attrs.xml | 44 ++ app/src/main/res/values/cardview_colors.xml | 26 ++ app/src/main/res/values/cardview_dimens.xml | 25 ++ app/src/main/res/values/cardview_styles.xml | 31 ++ 18 files changed, 1523 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/android/support/v7/widget/CardView.java create mode 100644 app/src/main/java/android/support/v7/widget/CardViewApi21.java create mode 100644 app/src/main/java/android/support/v7/widget/CardViewDelegate.java create mode 100644 app/src/main/java/android/support/v7/widget/CardViewEclairMr1.java create mode 100644 app/src/main/java/android/support/v7/widget/CardViewImpl.java create mode 100644 app/src/main/java/android/support/v7/widget/CardViewJellybeanMr1.java create mode 100644 app/src/main/java/android/support/v7/widget/RoundRectDrawable.java create mode 100644 app/src/main/java/android/support/v7/widget/RoundRectDrawableWithShadow.java create mode 100644 app/src/main/res/values/cardview_attrs.xml create mode 100644 app/src/main/res/values/cardview_colors.xml create mode 100644 app/src/main/res/values/cardview_dimens.xml create mode 100644 app/src/main/res/values/cardview_styles.xml diff --git a/app/app.iml b/app/app.iml index 6f25e0eb2..a90485f17 100644 --- a/app/app.iml +++ b/app/app.iml @@ -86,7 +86,6 @@ - diff --git a/app/build.gradle b/app/build.gradle index e31497466..accc84b69 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,7 +27,6 @@ dependencies { compile 'com.android.support:support-v4:22.0.0' compile 'com.android.support:appcompat-v7:22.0.0' compile 'com.android.support:recyclerview-v7:22.0.0' - compile 'com.android.support:cardview-v7:22.0.0' compile 'com.github.japgolly.android:svg-android:2.0.6' compile fileTree(dir: 'libs', include: ['*.jar']) } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index eaf543e5a..89023e551 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -44,6 +44,10 @@ void setProgress(float); float getProgress(); } +-keepclassmembers class com.hippo.ehviewer.widget.SearchBar { + void setProgress(float); + float getProgress(); +} #For VectorResources --keep class android.content.res.VectorResources {*;} +-keepclassmembers class android.content.res.VectorResources {*;} diff --git a/app/src/main/java/android/support/v7/widget/CardView.java b/app/src/main/java/android/support/v7/widget/CardView.java new file mode 100644 index 000000000..f1ecc16d5 --- /dev/null +++ b/app/src/main/java/android/support/v7/widget/CardView.java @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.support.v7.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.os.Build; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +import com.hippo.ehviewer.R; + +/** + * A FrameLayout with a rounded corner background and shadow. + *

+ * CardView uses elevation property on L for shadows and falls back to a custom shadow + * implementation on older platforms. + *

+ * Due to expensive nature of rounded corner clipping, on platforms before L, CardView does not + * clip its children that intersect with rounded corners. Instead, it adds padding to avoid such + * intersection (See {@link #setPreventCornerOverlap(boolean)} to change this behavior). + *

+ * Before L, CardView adds padding to its content and draws shadows to that area. This padding + * amount is equal to maxCardElevation + (1 - cos45) * cornerRadius on the sides and + * maxCardElevation * 1.5 + (1 - cos45) * cornerRadius on top and bottom. + *

+ * Since padding is used to offset content for shadows, you cannot set padding on CardView. + * Instead, + * you can use content padding attributes in XML or {@link #setContentPadding(int, int, int, int)} + * in code to set the padding between the edges of the Card and children of CardView. + *

+ * Note that, if you specify exact dimensions for the CardView, because of the shadows, its content + * area will be different between platforms before L and after L. By using api version specific + * resource values, you can avoid these changes. Alternatively, If you want CardView to add inner + * padding on platforms L and after as well, you can set {@link #setUseCompatPadding(boolean)} to + * true. + *

+ * To change CardView's elevation in a backward compatible way, use + * {@link #setCardElevation(float)}. CardView will use elevation API on L and before L, it will + * change the shadow size. To avoid moving the View while shadow size is changing, shadow size is + * clamped by {@link #getMaxCardElevation()}. If you want to change elevation dynamically, you + * should call {@link #setMaxCardElevation(float)} when CardView is initialized. + * + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardBackgroundColor + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardCornerRadius + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardElevation + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardMaxElevation + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardUseCompatPadding + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardPreventCornerOverlap + * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPadding + * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingLeft + * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingTop + * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingRight + * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingBottom + */ +public class CardView extends FrameLayout implements CardViewDelegate { + + private static final CardViewImpl NO_ELEVATION_IMPL; + private static final CardViewImpl IMPL; + + private CardViewImpl mImpl; + + static { + if (Build.VERSION.SDK_INT >= 21) { + IMPL = new CardViewApi21(); + NO_ELEVATION_IMPL = new CardViewJellybeanMr1(); + } else if (Build.VERSION.SDK_INT >= 17) { + IMPL = new CardViewJellybeanMr1(); + NO_ELEVATION_IMPL = IMPL; + } else { + IMPL = new CardViewEclairMr1(); + NO_ELEVATION_IMPL = IMPL; + } + IMPL.initStatic(); + NO_ELEVATION_IMPL.initStatic(); + } + + private boolean mCompatPadding; + + private boolean mPreventCornerOverlap; + + private final Rect mContentPadding = new Rect(); + + private final Rect mShadowBounds = new Rect(); + + + public CardView(Context context) { + super(context); + initialize(context, null, 0); + } + + public CardView(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context, attrs, 0); + } + + public CardView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initialize(context, attrs, defStyleAttr); + } + + @Override + public void setPadding(int left, int top, int right, int bottom) { + // NO OP + } + + @Override + public void setPaddingRelative(int start, int top, int end, int bottom) { + // NO OP + } + + /** + * Returns whether CardView will add inner padding on platforms L and after. + * + * @return True CardView adds inner padding on platforms L and after to have same dimensions + * with platforms before L. + */ + @Override + public boolean getUseCompatPadding() { + return mCompatPadding; + } + + /** + * CardView adds additional padding to draw shadows on platforms before L. + *

+ * This may cause Cards to have different sizes between L and before L. If you need to align + * CardView with other Views, you may need api version specific dimension resources to account + * for the changes. + * As an alternative, you can set this flag to true and CardView will add the same + * padding values on platforms L and after. + *

+ * Since setting this flag to true adds unnecessary gaps in the UI, default value is + * false. + * + * @param useCompatPadding True if CardView should add padding for the shadows on platforms L + * and above. + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardUseCompatPadding + */ + public void setUseCompatPadding(boolean useCompatPadding) { + if (mCompatPadding == useCompatPadding) { + return; + } + mCompatPadding = useCompatPadding; + mImpl.onCompatPaddingChanged(this); + } + + /** + * Sets the padding between the Card's edges and the children of CardView. + *

+ * Depending on platform version or {@link #getUseCompatPadding()} settings, CardView may + * update these values before calling {@link android.view.View#setPadding(int, int, int, int)}. + * + * @param left The left padding in pixels + * @param top The top padding in pixels + * @param right The right padding in pixels + * @param bottom The bottom padding in pixels + * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPadding + * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingLeft + * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingTop + * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingRight + * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingBottom + */ + public void setContentPadding(int left, int top, int right, int bottom) { + mContentPadding.set(left, top, right, bottom); + mImpl.updatePadding(this); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (!(mImpl instanceof CardViewApi21)) { + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + switch (widthMode) { + case MeasureSpec.EXACTLY: + case MeasureSpec.AT_MOST: + final int minWidth = (int) Math.ceil(mImpl.getMinWidth(this)); + widthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minWidth, + MeasureSpec.getSize(widthMeasureSpec)), widthMode); + break; + } + + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); + switch (heightMode) { + case MeasureSpec.EXACTLY: + case MeasureSpec.AT_MOST: + final int minHeight = (int) Math.ceil(mImpl.getMinHeight(this)); + heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minHeight, + MeasureSpec.getSize(heightMeasureSpec)), heightMode); + break; + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + + private void initialize(Context context, AttributeSet attrs, int defStyleAttr) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CardView, defStyleAttr, R.style.CardView_Light); + boolean forceNoElevation = a.getBoolean(R.styleable.CardView_forceNoElevation, false); + if (forceNoElevation) { + mImpl = NO_ELEVATION_IMPL; + } else { + mImpl = IMPL; + } + int backgroundColor = a.getColor(R.styleable.CardView_cardBackgroundColor, 0); + float radius = a.getDimension(R.styleable.CardView_cardCornerRadius, 0); + float elevation = a.getDimension(R.styleable.CardView_cardElevation, 0); + float maxElevation = a.getDimension(R.styleable.CardView_cardMaxElevation, 0); + mCompatPadding = a.getBoolean(R.styleable.CardView_cardUseCompatPadding, false); + mPreventCornerOverlap = a.getBoolean(R.styleable.CardView_cardPreventCornerOverlap, true); + int defaultPadding = a.getDimensionPixelSize(R.styleable.CardView_contentPadding, 0); + mContentPadding.left = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingLeft, + defaultPadding); + mContentPadding.top = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingTop, + defaultPadding); + mContentPadding.right = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingRight, + defaultPadding); + mContentPadding.bottom = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingBottom, + defaultPadding); + if (elevation > maxElevation) { + maxElevation = elevation; + } + a.recycle(); + mImpl.initialize(this, context, backgroundColor, radius, elevation, maxElevation); + } + + /** + * Updates the background color of the CardView + * + * @param color The new color to set for the card background + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardBackgroundColor + */ + public void setCardBackgroundColor(int color) { + mImpl.setBackgroundColor(this, color); + } + + /** + * Returns the inner padding after the Card's left edge + * + * @return the inner padding after the Card's left edge + */ + public int getContentPaddingLeft() { + return mContentPadding.left; + } + + /** + * Returns the inner padding before the Card's right edge + * + * @return the inner padding before the Card's right edge + */ + public int getContentPaddingRight() { + return mContentPadding.right; + } + + /** + * Returns the inner padding after the Card's top edge + * + * @return the inner padding after the Card's top edge + */ + public int getContentPaddingTop() { + return mContentPadding.top; + } + + /** + * Returns the inner padding before the Card's bottom edge + * + * @return the inner padding before the Card's bottom edge + */ + public int getContentPaddingBottom() { + return mContentPadding.bottom; + } + + /** + * Updates the corner radius of the CardView. + * + * @param radius The radius in pixels of the corners of the rectangle shape + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardCornerRadius + * @see #setRadius(float) + */ + public void setRadius(float radius) { + mImpl.setRadius(this, radius); + } + + /** + * Returns the corner radius of the CardView. + * + * @return Corner radius of the CardView + * @see #getRadius() + */ + @Override + public float getRadius() { + return mImpl.getRadius(this); + } + + /** + * Internal method used by CardView implementations to update the padding. + * + * @hide + */ + @Override + public void setShadowPadding(int left, int top, int right, int bottom) { + mShadowBounds.set(left, top, right, bottom); + super.setPadding(left + mContentPadding.left, top + mContentPadding.top, + right + mContentPadding.right, bottom + mContentPadding.bottom); + } + + /** + * Updates the backward compatible elevation of the CardView. + * + * @param radius The backward compatible elevation in pixels. + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardElevation + * @see #getCardElevation() + * @see #setMaxCardElevation(float) + */ + public void setCardElevation(float radius) { + mImpl.setElevation(this, radius); + } + + /** + * Returns the backward compatible elevation of the CardView. + * + * @return Elevation of the CardView + * @see #setCardElevation(float) + * @see #getMaxCardElevation() + */ + public float getCardElevation() { + return mImpl.getElevation(this); + } + + /** + * Updates the backward compatible elevation of the CardView. + *

+ * Calling this method has no effect if device OS version is L or newer and + * {@link #getUseCompatPadding()} is false. + * + * @param radius The backward compatible elevation in pixels. + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardElevation + * @see #setCardElevation(float) + * @see #getMaxCardElevation() + */ + public void setMaxCardElevation(float radius) { + mImpl.setMaxElevation(this, radius); + } + + /** + * Returns the backward compatible elevation of the CardView. + * + * @return Elevation of the CardView + * @see #setMaxCardElevation(float) + * @see #getCardElevation() + */ + public float getMaxCardElevation() { + return mImpl.getMaxElevation(this); + } + + /** + * Returns whether CardView should add extra padding to content to avoid overlaps with rounded + * corners on API versions 20 and below. + * + * @return True if CardView prevents overlaps with rounded corners on platforms before L. + * Default value is true. + */ + @Override + public boolean getPreventCornerOverlap() { + return mPreventCornerOverlap; + } + + /** + * On API 20 and before, CardView does not clip the bounds of the Card for the rounded corners. + * Instead, it adds padding to content so that it won't overlap with the rounded corners. + * You can disable this behavior by setting this field to false. + *

+ * Setting this value on API 21 and above does not have any effect unless you have enabled + * compatibility padding. + * + * @param preventCornerOverlap Whether CardView should add extra padding to content to avoid + * overlaps with the CardView corners. + * @attr ref android.support.v7.cardview.R.styleable#CardView_cardPreventCornerOverlap + * @see #setUseCompatPadding(boolean) + */ + public void setPreventCornerOverlap(boolean preventCornerOverlap) { + if (preventCornerOverlap == mPreventCornerOverlap) { + return; + } + mPreventCornerOverlap = preventCornerOverlap; + mImpl.onPreventCornerOverlapChanged(this); + } +} diff --git a/app/src/main/java/android/support/v7/widget/CardViewApi21.java b/app/src/main/java/android/support/v7/widget/CardViewApi21.java new file mode 100644 index 000000000..7bf98616b --- /dev/null +++ b/app/src/main/java/android/support/v7/widget/CardViewApi21.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.support.v7.widget; + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build; +import android.view.View; + +class CardViewApi21 implements CardViewImpl { + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public void initialize(CardViewDelegate cardView, Context context, int backgroundColor, + float radius, float elevation, float maxElevation) { + final RoundRectDrawable backgroundDrawable = new RoundRectDrawable(backgroundColor, radius); + cardView.setBackgroundDrawable(backgroundDrawable); + View view = (View) cardView; + view.setClipToOutline(true); + view.setElevation(elevation); + setMaxElevation(cardView, maxElevation); + } + + @Override + public void setRadius(CardViewDelegate cardView, float radius) { + ((RoundRectDrawable) (cardView.getBackground())).setRadius(radius); + } + + @Override + public void initStatic() { + } + + @Override + public void setMaxElevation(CardViewDelegate cardView, float maxElevation) { + ((RoundRectDrawable) (cardView.getBackground())).setPadding(maxElevation, + cardView.getUseCompatPadding(), cardView.getPreventCornerOverlap()); + updatePadding(cardView); + } + + @Override + public float getMaxElevation(CardViewDelegate cardView) { + return ((RoundRectDrawable) (cardView.getBackground())).getPadding(); + } + + @Override + public float getMinWidth(CardViewDelegate cardView) { + return getRadius(cardView) * 2; + } + + @Override + public float getMinHeight(CardViewDelegate cardView) { + return getRadius(cardView) * 2; + } + + @Override + public float getRadius(CardViewDelegate cardView) { + return ((RoundRectDrawable) (cardView.getBackground())).getRadius(); + } + + @Override + public void setElevation(CardViewDelegate cardView, float elevation) { + ((View) cardView).setElevation(elevation); + } + + @Override + public float getElevation(CardViewDelegate cardView) { + return ((View) cardView).getElevation(); + } + + @Override + public void updatePadding(CardViewDelegate cardView) { + if (!cardView.getUseCompatPadding()) { + cardView.setShadowPadding(0, 0, 0, 0); + return; + } + float elevation = getMaxElevation(cardView); + final float radius = getRadius(cardView); + int hPadding = (int) Math.ceil(RoundRectDrawableWithShadow + .calculateHorizontalPadding(elevation, radius, cardView.getPreventCornerOverlap())); + int vPadding = (int) Math.ceil(RoundRectDrawableWithShadow + .calculateVerticalPadding(elevation, radius, cardView.getPreventCornerOverlap())); + cardView.setShadowPadding(hPadding, vPadding, hPadding, vPadding); + } + + @Override + public void onCompatPaddingChanged(CardViewDelegate cardView) { + setMaxElevation(cardView, getMaxElevation(cardView)); + } + + @Override + public void onPreventCornerOverlapChanged(CardViewDelegate cardView) { + setMaxElevation(cardView, getMaxElevation(cardView)); + } + + @Override + public void setBackgroundColor(CardViewDelegate cardView, int color) { + ((RoundRectDrawable) (cardView.getBackground())).setColor(color); + } +} diff --git a/app/src/main/java/android/support/v7/widget/CardViewDelegate.java b/app/src/main/java/android/support/v7/widget/CardViewDelegate.java new file mode 100644 index 000000000..3d4006205 --- /dev/null +++ b/app/src/main/java/android/support/v7/widget/CardViewDelegate.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.support.v7.widget; + +import android.graphics.drawable.Drawable; + +/** + * Interface provided by CardView to implementations. + *

+ * Necessary to resolve circular dependency between base CardView and platform implementations. + */ +interface CardViewDelegate { + void setBackgroundDrawable(Drawable paramDrawable); + Drawable getBackground(); + boolean getUseCompatPadding(); + boolean getPreventCornerOverlap(); + float getRadius(); + void setShadowPadding(int left, int top, int right, int bottom); +} diff --git a/app/src/main/java/android/support/v7/widget/CardViewEclairMr1.java b/app/src/main/java/android/support/v7/widget/CardViewEclairMr1.java new file mode 100644 index 000000000..8f7adfcef --- /dev/null +++ b/app/src/main/java/android/support/v7/widget/CardViewEclairMr1.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.support.v7.widget; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.View; + +class CardViewEclairMr1 implements CardViewImpl { + + final RectF sCornerRect = new RectF(); + + @Override + public void initStatic() { + // Draws a round rect using 7 draw operations. This is faster than using + // canvas.drawRoundRect before JBMR1 because API 11-16 used alpha mask textures to draw + // shapes. + RoundRectDrawableWithShadow.sRoundRectHelper + = new RoundRectDrawableWithShadow.RoundRectHelper() { + @Override + public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius, + Paint paint) { + final float twoRadius = cornerRadius * 2; + final float innerWidth = bounds.width() - twoRadius - 1; + final float innerHeight = bounds.height() - twoRadius - 1; + // increment it to account for half pixels. + if (cornerRadius >= 1f) { + cornerRadius += .5f; + sCornerRect.set(-cornerRadius, -cornerRadius, cornerRadius, cornerRadius); + int saved = canvas.save(); + canvas.translate(bounds.left + cornerRadius, bounds.top + cornerRadius); + canvas.drawArc(sCornerRect, 180, 90, true, paint); + canvas.translate(innerWidth, 0); + canvas.rotate(90); + canvas.drawArc(sCornerRect, 180, 90, true, paint); + canvas.translate(innerHeight, 0); + canvas.rotate(90); + canvas.drawArc(sCornerRect, 180, 90, true, paint); + canvas.translate(innerWidth, 0); + canvas.rotate(90); + canvas.drawArc(sCornerRect, 180, 90, true, paint); + canvas.restoreToCount(saved); + //draw top and bottom pieces + canvas.drawRect(bounds.left + cornerRadius - 1f, bounds.top, + bounds.right - cornerRadius + 1f, bounds.top + cornerRadius, + paint); + canvas.drawRect(bounds.left + cornerRadius - 1f, + bounds.bottom - cornerRadius + 1f, bounds.right - cornerRadius + 1f, + bounds.bottom, paint); + } +//// center + canvas.drawRect(bounds.left, bounds.top + Math.max(0, cornerRadius - 1f), + bounds.right, bounds.bottom - cornerRadius + 1f, paint); + } + }; + } + + @Override + public void initialize(CardViewDelegate cardView, Context context, int backgroundColor, + float radius, float elevation, float maxElevation) { + RoundRectDrawableWithShadow background = createBackground(context, backgroundColor, radius, + elevation, maxElevation); + background.setAddPaddingForCorners(cardView.getPreventCornerOverlap()); + cardView.setBackgroundDrawable(background); + updatePadding(cardView); + } + + RoundRectDrawableWithShadow createBackground(Context context, int backgroundColor, + float radius, float elevation, float maxElevation) { + return new RoundRectDrawableWithShadow(context.getResources(), backgroundColor, radius, + elevation, maxElevation); + } + + @Override + public void updatePadding(CardViewDelegate cardView) { + Rect shadowPadding = new Rect(); + getShadowBackground(cardView).getMaxShadowAndCornerPadding(shadowPadding); + ((View) cardView).setMinimumHeight((int) Math.ceil(getMinHeight(cardView))); + ((View) cardView).setMinimumWidth((int) Math.ceil(getMinWidth(cardView))); + cardView.setShadowPadding(shadowPadding.left, shadowPadding.top, + shadowPadding.right, shadowPadding.bottom); + } + + @Override + public void onCompatPaddingChanged(CardViewDelegate cardView) { + // NO OP + } + + @Override + public void onPreventCornerOverlapChanged(CardViewDelegate cardView) { + getShadowBackground(cardView).setAddPaddingForCorners(cardView.getPreventCornerOverlap()); + updatePadding(cardView); + } + + @Override + public void setBackgroundColor(CardViewDelegate cardView, int color) { + getShadowBackground(cardView).setColor(color); + } + + @Override + public void setRadius(CardViewDelegate cardView, float radius) { + getShadowBackground(cardView).setCornerRadius(radius); + updatePadding(cardView); + } + + @Override + public float getRadius(CardViewDelegate cardView) { + return getShadowBackground(cardView).getCornerRadius(); + } + + @Override + public void setElevation(CardViewDelegate cardView, float elevation) { + getShadowBackground(cardView).setShadowSize(elevation); + } + + @Override + public float getElevation(CardViewDelegate cardView) { + return getShadowBackground(cardView).getShadowSize(); + } + + @Override + public void setMaxElevation(CardViewDelegate cardView, float maxElevation) { + getShadowBackground(cardView).setMaxShadowSize(maxElevation); + updatePadding(cardView); + } + + @Override + public float getMaxElevation(CardViewDelegate cardView) { + return getShadowBackground(cardView).getMaxShadowSize(); + } + + @Override + public float getMinWidth(CardViewDelegate cardView) { + return getShadowBackground(cardView).getMinWidth(); + } + + @Override + public float getMinHeight(CardViewDelegate cardView) { + return getShadowBackground(cardView).getMinHeight(); + } + + private RoundRectDrawableWithShadow getShadowBackground(CardViewDelegate cardView) { + return ((RoundRectDrawableWithShadow) cardView.getBackground()); + } +} diff --git a/app/src/main/java/android/support/v7/widget/CardViewImpl.java b/app/src/main/java/android/support/v7/widget/CardViewImpl.java new file mode 100644 index 000000000..9fbf09f0b --- /dev/null +++ b/app/src/main/java/android/support/v7/widget/CardViewImpl.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.support.v7.widget; + +import android.content.Context; +/** + * Interface for platform specific CardView implementations. + */ +interface CardViewImpl { + void initialize(CardViewDelegate cardView, Context context, int backgroundColor, float radius, float elevation, float maxElevation); + + void setRadius(CardViewDelegate cardView, float radius); + + float getRadius(CardViewDelegate cardView); + + void setElevation(CardViewDelegate cardView, float elevation); + + float getElevation(CardViewDelegate cardView); + + void initStatic(); + + void setMaxElevation(CardViewDelegate cardView, float maxElevation); + + float getMaxElevation(CardViewDelegate cardView); + + float getMinWidth(CardViewDelegate cardView); + + float getMinHeight(CardViewDelegate cardView); + + void updatePadding(CardViewDelegate cardView); + + void onCompatPaddingChanged(CardViewDelegate cardView); + + void onPreventCornerOverlapChanged(CardViewDelegate cardView); + + void setBackgroundColor(CardViewDelegate cardView, int color); +} diff --git a/app/src/main/java/android/support/v7/widget/CardViewJellybeanMr1.java b/app/src/main/java/android/support/v7/widget/CardViewJellybeanMr1.java new file mode 100644 index 000000000..bb38a3c29 --- /dev/null +++ b/app/src/main/java/android/support/v7/widget/CardViewJellybeanMr1.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.support.v7.widget; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; + +class CardViewJellybeanMr1 extends CardViewEclairMr1 { + + @Override + public void initStatic() { + RoundRectDrawableWithShadow.sRoundRectHelper + = new RoundRectDrawableWithShadow.RoundRectHelper() { + @Override + public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius, + Paint paint) { + canvas.drawRoundRect(bounds, cornerRadius, cornerRadius, paint); + } + }; + } +} \ No newline at end of file diff --git a/app/src/main/java/android/support/v7/widget/RoundRectDrawable.java b/app/src/main/java/android/support/v7/widget/RoundRectDrawable.java new file mode 100644 index 000000000..347776186 --- /dev/null +++ b/app/src/main/java/android/support/v7/widget/RoundRectDrawable.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.support.v7.widget; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; + +import static android.support.v7.widget.RoundRectDrawableWithShadow.calculateVerticalPadding; +import static android.support.v7.widget.RoundRectDrawableWithShadow.calculateHorizontalPadding; + +/** + * Very simple drawable that draws a rounded rectangle background with arbitrary corners and also + * reports proper outline for L. + *

+ * Simpler and uses less resources compared to GradientDrawable or ShapeDrawable. + */ +class RoundRectDrawable extends Drawable { + private float mRadius; + private final Paint mPaint; + private final RectF mBoundsF; + private final Rect mBoundsI; + private float mPadding; + private boolean mInsetForPadding = false; + private boolean mInsetForRadius = true; + + public RoundRectDrawable(int backgroundColor, float radius) { + mRadius = radius; + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); + mPaint.setColor(backgroundColor); + mBoundsF = new RectF(); + mBoundsI = new Rect(); + } + + void setPadding(float padding, boolean insetForPadding, boolean insetForRadius) { + if (padding == mPadding && mInsetForPadding == insetForPadding && + mInsetForRadius == insetForRadius) { + return; + } + mPadding = padding; + mInsetForPadding = insetForPadding; + mInsetForRadius = insetForRadius; + updateBounds(null); + invalidateSelf(); + } + + float getPadding() { + return mPadding; + } + + @Override + public void draw(Canvas canvas) { + canvas.drawRoundRect(mBoundsF, mRadius, mRadius, mPaint); + } + + private void updateBounds(Rect bounds) { + if (bounds == null) { + bounds = getBounds(); + } + mBoundsF.set(bounds.left, bounds.top, bounds.right, bounds.bottom); + mBoundsI.set(bounds); + if (mInsetForPadding) { + float vInset = calculateVerticalPadding(mPadding, mRadius, mInsetForRadius); + float hInset = calculateHorizontalPadding(mPadding, mRadius, mInsetForRadius); + mBoundsI.inset((int) Math.ceil(hInset), (int) Math.ceil(vInset)); + // to make sure they have same bounds. + mBoundsF.set(mBoundsI); + } + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + updateBounds(bounds); + } + + @Override + public void getOutline(Outline outline) { + outline.setRoundRect(mBoundsI, mRadius); + } + + void setRadius(float radius) { + if (radius == mRadius) { + return; + } + mRadius = radius; + updateBounds(null); + invalidateSelf(); + } + + @Override + public void setAlpha(int alpha) { + // not supported because older versions do not support + } + + @Override + public void setColorFilter(ColorFilter cf) { + // not supported because older versions do not support + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + public float getRadius() { + return mRadius; + } + + public void setColor(int color) { + mPaint.setColor(color); + invalidateSelf(); + } +} diff --git a/app/src/main/java/android/support/v7/widget/RoundRectDrawableWithShadow.java b/app/src/main/java/android/support/v7/widget/RoundRectDrawableWithShadow.java new file mode 100644 index 000000000..226d47fea --- /dev/null +++ b/app/src/main/java/android/support/v7/widget/RoundRectDrawableWithShadow.java @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.support.v7.widget; + +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.RadialGradient; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; + +import com.hippo.ehviewer.R; + +/** + * A rounded rectangle drawable which also includes a shadow around. + */ +class RoundRectDrawableWithShadow extends Drawable { + // used to calculate content padding + final static double COS_45 = Math.cos(Math.toRadians(45)); + + final static float SHADOW_MULTIPLIER = 1.5f; + + final int mInsetShadow; // extra shadow to avoid gaps between card and shadow + + /* + * This helper is set by CardView implementations. + *

+ * Prior to API 17, canvas.drawRoundRect is expensive; which is why we need this interface + * to draw efficient rounded rectangles before 17. + * */ + static RoundRectHelper sRoundRectHelper; + + Paint mPaint; + + Paint mCornerShadowPaint; + + Paint mEdgeShadowPaint; + + final RectF mCardBounds; + + float mCornerRadius; + + Path mCornerShadowPath; + + // updated value with inset + float mMaxShadowSize; + + // actual value set by developer + float mRawMaxShadowSize; + + // multiplied value to account for shadow offset + float mShadowSize; + + // actual value set by developer + float mRawShadowSize; + + private boolean mDirty = true; + + private final int mShadowStartColor; + + private final int mShadowEndColor; + + private boolean mAddPaddingForCorners = true; + + /** + * If shadow size is set to a value above max shadow, we print a warning + */ + private boolean mPrintedShadowClipWarning = false; + + RoundRectDrawableWithShadow(Resources resources, int backgroundColor, float radius, + float shadowSize, float maxShadowSize) { + mShadowStartColor = resources.getColor(R.color.cardview_shadow_start_color); + mShadowEndColor = resources.getColor(R.color.cardview_shadow_end_color); + mInsetShadow = resources.getDimensionPixelSize(R.dimen.cardview_compat_inset_shadow); + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); + mPaint.setColor(backgroundColor); + mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); + mCornerShadowPaint.setStyle(Paint.Style.FILL); + mCornerRadius = (int) (radius + .5f); + mCardBounds = new RectF(); + mEdgeShadowPaint = new Paint(mCornerShadowPaint); + mEdgeShadowPaint.setAntiAlias(false); + setShadowSize(shadowSize, maxShadowSize); + } + + /** + * Casts the value to an even integer. + */ + private int toEven(float value) { + int i = (int) (value + .5f); + if (i % 2 == 1) { + return i - 1; + } + return i; + } + + public void setAddPaddingForCorners(boolean addPaddingForCorners) { + mAddPaddingForCorners = addPaddingForCorners; + invalidateSelf(); + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + mCornerShadowPaint.setAlpha(alpha); + mEdgeShadowPaint.setAlpha(alpha); + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + mDirty = true; + } + + void setShadowSize(float shadowSize, float maxShadowSize) { + if (shadowSize < 0 || maxShadowSize < 0) { + throw new IllegalArgumentException("invalid shadow size"); + } + shadowSize = toEven(shadowSize); + maxShadowSize = toEven(maxShadowSize); + if (shadowSize > maxShadowSize) { + shadowSize = maxShadowSize; + if (!mPrintedShadowClipWarning) { + mPrintedShadowClipWarning = true; + } + } + if (mRawShadowSize == shadowSize && mRawMaxShadowSize == maxShadowSize) { + return; + } + mRawShadowSize = shadowSize; + mRawMaxShadowSize = maxShadowSize; + mShadowSize = (int)(shadowSize * SHADOW_MULTIPLIER + mInsetShadow + .5f); + mMaxShadowSize = maxShadowSize + mInsetShadow; + mDirty = true; + invalidateSelf(); + } + + @Override + public boolean getPadding(Rect padding) { + int vOffset = (int) Math.ceil(calculateVerticalPadding(mRawMaxShadowSize, mCornerRadius, + mAddPaddingForCorners)); + int hOffset = (int) Math.ceil(calculateHorizontalPadding(mRawMaxShadowSize, mCornerRadius, + mAddPaddingForCorners)); + padding.set(hOffset, vOffset, hOffset, vOffset); + return true; + } + + static float calculateVerticalPadding(float maxShadowSize, float cornerRadius, + boolean addPaddingForCorners) { + if (addPaddingForCorners) { + return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius); + } else { + return maxShadowSize * SHADOW_MULTIPLIER; + } + } + + static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius, + boolean addPaddingForCorners) { + if (addPaddingForCorners) { + return (float) (maxShadowSize + (1 - COS_45) * cornerRadius); + } else { + return maxShadowSize; + } + } + + @Override + public void setColorFilter(ColorFilter cf) { + mPaint.setColorFilter(cf); + mCornerShadowPaint.setColorFilter(cf); + mEdgeShadowPaint.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + void setCornerRadius(float radius) { + radius = (int) (radius + .5f); + if (mCornerRadius == radius) { + return; + } + mCornerRadius = radius; + mDirty = true; + invalidateSelf(); + } + + @Override + public void draw(Canvas canvas) { + if (mDirty) { + buildComponents(getBounds()); + mDirty = false; + } + canvas.translate(0, mRawShadowSize / 2); + drawShadow(canvas); + canvas.translate(0, -mRawShadowSize / 2); + sRoundRectHelper.drawRoundRect(canvas, mCardBounds, mCornerRadius, mPaint); + } + + private void drawShadow(Canvas canvas) { + final float edgeShadowTop = -mCornerRadius - mShadowSize; + final float inset = mCornerRadius + mInsetShadow + mRawShadowSize / 2; + final boolean drawHorizontalEdges = mCardBounds.width() - 2 * inset > 0; + final boolean drawVerticalEdges = mCardBounds.height() - 2 * inset > 0; + // LT + int saved = canvas.save(); + canvas.translate(mCardBounds.left + inset, mCardBounds.top + inset); + canvas.drawPath(mCornerShadowPath, mCornerShadowPaint); + if (drawHorizontalEdges) { + canvas.drawRect(0, edgeShadowTop, + mCardBounds.width() - 2 * inset, -mCornerRadius, + mEdgeShadowPaint); + } + canvas.restoreToCount(saved); + // RB + saved = canvas.save(); + canvas.translate(mCardBounds.right - inset, mCardBounds.bottom - inset); + canvas.rotate(180f); + canvas.drawPath(mCornerShadowPath, mCornerShadowPaint); + if (drawHorizontalEdges) { + canvas.drawRect(0, edgeShadowTop, + mCardBounds.width() - 2 * inset, -mCornerRadius + mShadowSize, + mEdgeShadowPaint); + } + canvas.restoreToCount(saved); + // LB + saved = canvas.save(); + canvas.translate(mCardBounds.left + inset, mCardBounds.bottom - inset); + canvas.rotate(270f); + canvas.drawPath(mCornerShadowPath, mCornerShadowPaint); + if (drawVerticalEdges) { + canvas.drawRect(0, edgeShadowTop, + mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint); + } + canvas.restoreToCount(saved); + // RT + saved = canvas.save(); + canvas.translate(mCardBounds.right - inset, mCardBounds.top + inset); + canvas.rotate(90f); + canvas.drawPath(mCornerShadowPath, mCornerShadowPaint); + if (drawVerticalEdges) { + canvas.drawRect(0, edgeShadowTop, + mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint); + } + canvas.restoreToCount(saved); + } + + private void buildShadowCorners() { + RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius); + RectF outerBounds = new RectF(innerBounds); + outerBounds.inset(-mShadowSize, -mShadowSize); + + if (mCornerShadowPath == null) { + mCornerShadowPath = new Path(); + } else { + mCornerShadowPath.reset(); + } + mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD); + mCornerShadowPath.moveTo(-mCornerRadius, 0); + mCornerShadowPath.rLineTo(-mShadowSize, 0); + // outer arc + mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false); + // inner arc + mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false); + mCornerShadowPath.close(); + float startRatio = mCornerRadius / (mCornerRadius + mShadowSize); + mCornerShadowPaint.setShader(new RadialGradient(0, 0, mCornerRadius + mShadowSize, + new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor}, + new float[]{0f, startRatio, 1f} + , Shader.TileMode.CLAMP)); + + // we offset the content shadowSize/2 pixels up to make it more realistic. + // this is why edge shadow shader has some extra space + // When drawing bottom edge shadow, we use that extra space. + mEdgeShadowPaint.setShader(new LinearGradient(0, -mCornerRadius + mShadowSize, 0, + -mCornerRadius - mShadowSize, + new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor}, + new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP)); + mEdgeShadowPaint.setAntiAlias(false); + } + + private void buildComponents(Rect bounds) { + // Card is offset SHADOW_MULTIPLIER * maxShadowSize to account for the shadow shift. + // We could have different top-bottom offsets to avoid extra gap above but in that case + // center aligning Views inside the CardView would be problematic. + final float verticalOffset = mRawMaxShadowSize * SHADOW_MULTIPLIER; + mCardBounds.set(bounds.left + mRawMaxShadowSize, bounds.top + verticalOffset, + bounds.right - mRawMaxShadowSize, bounds.bottom - verticalOffset); + buildShadowCorners(); + } + + float getCornerRadius() { + return mCornerRadius; + } + + void getMaxShadowAndCornerPadding(Rect into) { + getPadding(into); + } + + void setShadowSize(float size) { + setShadowSize(size, mRawMaxShadowSize); + } + + void setMaxShadowSize(float size) { + setShadowSize(mRawShadowSize, size); + } + + float getShadowSize() { + return mRawShadowSize; + } + + float getMaxShadowSize() { + return mRawMaxShadowSize; + } + + float getMinWidth() { + final float content = 2 * + Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow + mRawMaxShadowSize / 2); + return content + (mRawMaxShadowSize + mInsetShadow) * 2; + } + + float getMinHeight() { + final float content = 2 * Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow + + mRawMaxShadowSize * SHADOW_MULTIPLIER / 2); + return content + (mRawMaxShadowSize * SHADOW_MULTIPLIER + mInsetShadow) * 2; + } + + public void setColor(int color) { + mPaint.setColor(color); + invalidateSelf(); + } + + static interface RoundRectHelper { + void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius, Paint paint); + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java b/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java index 599aa4299..6c581cbcc 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java @@ -15,10 +15,15 @@ package com.hippo.ehviewer.widget; +import android.animation.Animator; import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Path; import android.graphics.Typeface; import android.os.Build; +import android.support.annotation.NonNull; import android.support.v7.widget.CardView; import android.text.TextUtils; import android.util.AttributeSet; @@ -27,31 +32,50 @@ import android.view.LayoutInflater; import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; +import android.widget.ArrayAdapter; +import android.widget.ListView; import android.widget.TextView; +import com.hippo.animation.SimpleAnimatorListener; import com.hippo.drawable.AddDeleteDrawable; import com.hippo.drawable.DrawerArrowDrawable; import com.hippo.ehviewer.R; +import com.hippo.util.MathUtils; import com.hippo.util.UiUtils; import com.hippo.util.ViewUtils; import com.hippo.widget.SimpleImageView; +import java.util.ArrayList; +import java.util.List; + public class SearchBar extends CardView implements View.OnClickListener, TextView.OnEditorActionListener { private static final long ANIMATION_TIME = 300; + private Path mPath = new Path(); + private int mWidth; + private int mHeight; + private int mBaseHeight; + private float mProgress; + private SimpleImageView mMenuButton; private TextView mLogoTextView; private SimpleImageView mActionButton; private SearchEditText mEditText; + private ListView mList; private DrawerArrowDrawable mDrawerArrowDrawable; private AddDeleteDrawable mAddDeleteDrawable; + private List mSuggestionList; + private ArrayAdapter mSuggestionAdapter; + private Helper mHelper; private boolean mInEditMode = false; + private boolean mInAnimation = false; + private boolean mFirstLayout = true; public SearchBar(Context context) { super(context); @@ -71,6 +95,7 @@ public class SearchBar extends CardView implements View.OnClickListener, private void init(Context context) { setRadius(UiUtils.dp2pix(context, 2)); setCardElevation(UiUtils.dp2pix(context, 2)); + setCardBackgroundColor(Color.WHITE); setOnClickListener(this); LayoutInflater.from(context).inflate(R.layout.widget_search_bar, this); @@ -78,6 +103,7 @@ public class SearchBar extends CardView implements View.OnClickListener, mLogoTextView = (TextView) findViewById(R.id.logo); mActionButton = (SimpleImageView) findViewById(R.id.action); mEditText = (SearchEditText) findViewById(R.id.search_edit_text); + mList = (ListView) findViewById(R.id.search_bar_list); mDrawerArrowDrawable = new DrawerArrowDrawable(getContext()); mAddDeleteDrawable = new AddDeleteDrawable(getContext()); @@ -89,6 +115,15 @@ public class SearchBar extends CardView implements View.OnClickListener, mActionButton.setOnClickListener(this); mEditText.setSearchBar(this); mEditText.setOnEditorActionListener(this); + + mSuggestionList = new ArrayList<>(); + mSuggestionList.add("aaaaaaaaaaaaaaaaaaaaa"); + mSuggestionList.add("dfsgfewrewfes"); + mSuggestionList.add("dehyklrthrhbdsrf"); + mSuggestionList.add("粉色热污染份额为别人的"); + mSuggestionAdapter = new ArrayAdapter<>(getContext(), + android.R.layout.simple_list_item_1, mSuggestionList); + mList.setAdapter(mSuggestionAdapter); } public void setHelper(Helper helper) { @@ -144,12 +179,28 @@ public class SearchBar extends CardView implements View.OnClickListener, oa1.setDuration(ANIMATION_TIME); ObjectAnimator oa2 = ObjectAnimator.ofFloat(mAddDeleteDrawable, "progress", 0f, 1f); oa2.setDuration(ANIMATION_TIME); + ObjectAnimator oa3 = ObjectAnimator.ofFloat(this, "progress", 0f, 1f); + oa3.setDuration(ANIMATION_TIME); + oa3.addListener(new SimpleAnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + ViewUtils.setVisibility(mList, View.VISIBLE); + mInAnimation = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + mInAnimation = false; + } + }); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { oa1.setAutoCancel(true); oa2.setAutoCancel(true); + oa3.setAutoCancel(true); } oa1.start(); oa2.start(); + oa3.start(); } } @@ -167,12 +218,67 @@ public class SearchBar extends CardView implements View.OnClickListener, oa1.setDuration(ANIMATION_TIME); ObjectAnimator oa2 = ObjectAnimator.ofFloat(mAddDeleteDrawable, "progress", 1f, 0f); oa2.setDuration(ANIMATION_TIME); + ObjectAnimator oa3 = ObjectAnimator.ofFloat(this, "progress", 1f, 0f); + oa3.setDuration(ANIMATION_TIME); + oa3.addListener(new SimpleAnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + mInAnimation = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + ViewUtils.setVisibility(mList, View.GONE); + mInAnimation = false; + } + }); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { oa1.setAutoCancel(true); oa2.setAutoCancel(true); + oa3.setAutoCancel(true); } oa1.start(); oa2.start(); + oa3.start(); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (mInEditMode) { + mWidth = right - left; + mHeight = bottom - top; + } + if (mFirstLayout) { + mFirstLayout = false; + mBaseHeight = bottom - top; + } + } + + @SuppressWarnings("unused") + public void setProgress(float progress) { + mProgress = progress; + invalidate(); + } + + @SuppressWarnings("unused") + public float getProgress() { + return mProgress; + } + + @Override + public void draw(@NonNull Canvas canvas) { + if (mInAnimation) { + final int state = canvas.save(); + float bottom = MathUtils.lerp(mBaseHeight, mHeight, mProgress); + mPath.rewind(); + mPath.addRect(0f, 0f, mWidth, bottom, Path.Direction.CW); + canvas.clipPath(mPath); + super.draw(canvas); + canvas.restoreToCount(state); + } else { + super.draw(canvas); } } diff --git a/app/src/main/res/layout/scene_gallery_list.xml b/app/src/main/res/layout/scene_gallery_list.xml index c1d93dae1..c3c43a625 100644 --- a/app/src/main/res/layout/scene_gallery_list.xml +++ b/app/src/main/res/layout/scene_gallery_list.xml @@ -18,17 +18,19 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - + + diff --git a/app/src/main/res/layout/widget_search_bar.xml b/app/src/main/res/layout/widget_search_bar.xml index 467641896..1419d2a98 100644 --- a/app/src/main/res/layout/widget_search_bar.xml +++ b/app/src/main/res/layout/widget_search_bar.xml @@ -19,7 +19,7 @@ + android:layout_height="wrap_content" + android:visibility="gone"/> diff --git a/app/src/main/res/values/cardview_attrs.xml b/app/src/main/res/values/cardview_attrs.xml new file mode 100644 index 000000000..0838ac57c --- /dev/null +++ b/app/src/main/res/values/cardview_attrs.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/cardview_colors.xml b/app/src/main/res/values/cardview_colors.xml new file mode 100644 index 000000000..3ed7087fe --- /dev/null +++ b/app/src/main/res/values/cardview_colors.xml @@ -0,0 +1,26 @@ + + + + + + #FFFAFAFA + + #FF202020 + + #37000000 + + #03000000 + \ No newline at end of file diff --git a/app/src/main/res/values/cardview_dimens.xml b/app/src/main/res/values/cardview_dimens.xml new file mode 100644 index 000000000..ebfbb3a97 --- /dev/null +++ b/app/src/main/res/values/cardview_dimens.xml @@ -0,0 +1,25 @@ + + + + + + 2dp + + 2dp + + 1dp + \ No newline at end of file diff --git a/app/src/main/res/values/cardview_styles.xml b/app/src/main/res/values/cardview_styles.xml new file mode 100644 index 000000000..99dfbc98c --- /dev/null +++ b/app/src/main/res/values/cardview_styles.xml @@ -0,0 +1,31 @@ + + + + + + + \ No newline at end of file