Add SearchBar list animation
This commit is contained in:
1 parent
708ba3a3e9
commit
beec5d8776
18 files changed
+1523
-11
No files matched your search
@@ -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.
|
||||
* <p>
|
||||
* CardView uses <code>elevation</code> property on L for shadows and falls back to a custom shadow
|
||||
* implementation on older platforms.
|
||||
* <p>
|
||||
* 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).
|
||||
* <p>
|
||||
* Before L, CardView adds padding to its content and draws shadows to that area. This padding
|
||||
* amount is equal to <code>maxCardElevation + (1 - cos45) * cornerRadius</code> on the sides and
|
||||
* <code>maxCardElevation * 1.5 + (1 - cos45) * cornerRadius</code> on top and bottom.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
* <code>true</code>.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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 <code>true</code> and CardView will add the same
|
||||
* padding values on platforms L and after.
|
||||
* <p>
|
||||
* Since setting this flag to true adds unnecessary gaps in the UI, default value is
|
||||
* <code>false</code>.
|
||||
*
|
||||
* @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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Calling this method has no effect if device OS version is L or newer and
|
||||
* {@link #getUseCompatPadding()} is <code>false</code>.
|
||||
*
|
||||
* @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 <code>true</code>.
|
||||
*/
|
||||
@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 <code>false</code>.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user