Android RecyclerView 首條懸停 可加動畫
阿新 • • 發佈:2018-12-27
我是看的大佬的 Demo
https://me.csdn.net/qq_30447263
這個的應用場景很多,除了動畫 自己需要摸索 就是這個Behavior延伸了
此處為動畫
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.BaseOnOffsetChangedListener() { @Override public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { //下降分度 float fraction = -verticalOffset * 1.f / (mIdTvMoving.getHeight() - mIdTvMoving.getMinHeight()); int color = ColUtils.evaluateColor(fraction, 0xffF07054, 0xff3F51B5); mIdTvMoving.setBackgroundColor(color);//顏色過渡 mIdTvMoving.setTextSize(40 * (1 - fraction));//字號縮小 mIdTvMoving.setTranslationX(-fraction * mIdTvMoving.getWidth());//X平移 } });
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> <android.support.design.widget.AppBarLayout android:id="@+id/abl_top" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/id_tv_moving" style="@style/TVTestCenter" android:text="Flag" app:layout_scrollFlags="scroll|snap"/> <TextView style="@style/TVTestCenter" android:text="Flag" app:layout_scrollFlags="scroll|enterAlwaysCollapsed"/> <TextView android:id="@+id/tvv22" style="@style/TVTestCenter" android:text="Flag" app:layout_scrollFlags="scroll|exitUntilCollapsed"/> <TextView style="@style/TVTestCenter" android:text="Flag" app:layout_scrollFlags="scroll|enterAlways"/> </android.support.design.widget.AppBarLayout> <android.support.v7.widget.RecyclerView android:id="@+id/recycler" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> <!--: elevation和pressedTranslationZ,分別為普通狀態下的陰影大小和點選時陰影的大小--> <!--寬高包裹內容 百分百居中--> <!--app:borderWidth="4dp" 不要這個 才有陰影--> <android.support.design.widget.FloatingActionButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="16dp" android:backgroundTint="@color/colorAccent" android:clickable="true" android:focusable="true" android:src="@drawable/appmanager" app:borderWidth="4dp" app:elevation="8dp" app:fabSize="auto" app:layout_anchor="@id/id_tv_moving" app:layout_anchorGravity="bottom|end" app:pressedTranslationZ="16dp" app:rippleColor="@color/colorPrimaryDark"/> </android.support.design.widget.CoordinatorLayout>
public class ColUtils { /** * 改變一個顏色的透明度 * * @param color * @param alpha */ public static int changeColorAlpha(int color, int alpha) { int a = (color >> 24) & 0xFF; a = (int) (a * (alpha / 255f)); int r = (color >> 16) & 0xFF; int g = (color >> 8) & 0xFF; int b = color & 0xFF; return (a << 24) | (r << 16) | (g << 8) | b; } /** * 返回隨機顏色 * * @return 隨機顏色 */ public static int randomColor() { Random random = new Random(); int a = 250 + random.nextInt(255); int r = 30 + random.nextInt(200); int g = 30 + random.nextInt(200); int b = 30 + random.nextInt(200); int randomColor = Color.argb(a, r, g, b); return randomColor; } /** * 返回隨機顏色 * * @return 隨機顏色 */ public static int randomRGB() { Random random = new Random(); int r = 30 + random.nextInt(200); int g = 30 + random.nextInt(200); int b = 30 + random.nextInt(200); return Color.rgb(r, g, b); } /** * 顏色變換 * * @param fraction 分度值 * @param startValue 開始色 * @param endValue 結束色 * @return 分度值處顏色 */ public static int evaluateColor(float fraction, int startValue, int endValue) { int startA = (startValue >> 24) & 0xff; int startR = (startValue >> 16) & 0xff; int startG = (startValue >> 8) & 0xff; int startB = startValue & 0xff; int endA = (endValue >> 24) & 0xff; int endR = (endValue >> 16) & 0xff; int endG = (endValue >> 8) & 0xff; int endB = endValue & 0xff; return (startA + (int) (fraction * (endA - startA)) << 24) | (startR + (int) (fraction * (endR - startR)) << 16) | (startG + (int) (fraction * (endG - startG)) << 8) | startB + (int) (fraction * (endB - startB)); } private static final int ENABLE_ATTR = android.R.attr.state_enabled; private static final int CHECKED_ATTR = android.R.attr.state_checked; private static final int PRESSED_ATTR = android.R.attr.state_pressed; /** * @param tintColor * @return */ public static ColorStateList generateThumbColorWithTintColor(final int tintColor) { int[][] states = new int[][]{ {-ENABLE_ATTR, CHECKED_ATTR}, {-ENABLE_ATTR}, {PRESSED_ATTR, -CHECKED_ATTR}, {PRESSED_ATTR, CHECKED_ATTR}, {CHECKED_ATTR}, {-CHECKED_ATTR} }; int[] colors = new int[]{ tintColor - 0xAA000000, 0xFFBABABA, tintColor - 0x99000000, tintColor - 0x99000000, tintColor | 0xFF000000, 0xFFEEEEEE }; return new ColorStateList(states, colors); } /** * @param tintColor * @return */ public static ColorStateList generateBackColorWithTintColor(final int tintColor) { int[][] states = new int[][]{ {-ENABLE_ATTR, CHECKED_ATTR}, {-ENABLE_ATTR}, {CHECKED_ATTR, PRESSED_ATTR}, {-CHECKED_ATTR, PRESSED_ATTR}, {CHECKED_ATTR}, {-CHECKED_ATTR} }; int[] colors = new int[]{ tintColor - 0xE1000000, 0x10000000, tintColor - 0xD0000000, 0x20000000, tintColor - 0xD0000000, 0x20000000 }; return new ColorStateList(states, colors); } /** * 顏色加深處理 * * @param RGBValues RGB的值,由alpha(透明度)、red(紅)、green(綠)、blue(藍)構成, * Android中我們一般使用它的16進位制, * 例如:"#FFAABBCC",最左邊到最右每兩個字母就是代表alpha(透明度)、 * red(紅)、green(綠)、blue(藍)。每種顏色值佔一個位元組(8位),值域0~255 * 所以下面使用移位的方法可以得到每種顏色的值,然後每種顏色值減小一下,在合成RGB顏色,顏色就會看起來深一些了 * @return */ public static int colorBurn(int RGBValues) { int red = RGBValues >> 16 & 0xFF; int green = RGBValues >> 8 & 0xFF; int blue = RGBValues & 0xFF; red = (int) Math.floor(red * (1 - 0.1)); green = (int) Math.floor(green * (1 - 0.1)); blue = (int) Math.floor(blue * (1 - 0.1)); return Color.rgb(red, green, blue); } }
可以設定 開始的顏色 ---> 結束的顏色
沒多少程式碼 ~ 幾乎都是粘的
然後是Behavior
這個效果可以應用在 底部導航欄 當然要求
這個效果寫在一些小介面 可以 ,完全體的導航欄還是去=>>github
public abstract class VerticalScrollingBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
private int mTotalDyUnconsumed = 0;
private int mTotalDyConsumed = 0;
private int mTotalDy = 0;
@ScrollDirection
private int mScrollDirection = ScrollDirection.SCROLL_NONE;
@ScrollDirection
private int mPreScrollDirection = ScrollDirection.SCROLL_NONE;
@ScrollDirection
private int mConsumedScrollDirection = ScrollDirection.SCROLL_NONE;
public VerticalScrollingBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerticalScrollingBehavior() {
super();
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({ScrollDirection.SCROLL_DIRECTION_UP, ScrollDirection.SCROLL_DIRECTION_DOWN})
public @interface ScrollDirection {
int SCROLL_DIRECTION_UP = 1;
int SCROLL_DIRECTION_DOWN = -1;
int SCROLL_NONE = 0;
}
/**
* @return Scroll direction: SCROLL_DIRECTION_UP, CROLL_DIRECTION_DOWN, SCROLL_NONE
*/
@ScrollDirection
public int getScrollDirection() {
return mScrollDirection;
}
/**
* @return ConsumedScroll direction: SCROLL_DIRECTION_UP, CROLL_DIRECTION_DOWN, SCROLL_NONE
*/
@ScrollDirection
public int getConsumedScrollDirection() {
return mConsumedScrollDirection;
}
/**
* @return PreScroll direction: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN, SCROLL_NONE
*/
@ScrollDirection
public int getPreScrollDirection() {
return mPreScrollDirection;
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) {
return (nestedScrollAxes & View.SCROLL_AXIS_VERTICAL) != 0;
}
// @Override
// public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) {
// super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
// }
//
// @Override
// public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
// super.onStopNestedScroll(coordinatorLayout, child, target);
// }
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyUnconsumed > 0 && mTotalDyUnconsumed < 0) {
mTotalDyUnconsumed = 0;
mScrollDirection = ScrollDirection.SCROLL_DIRECTION_UP;
onNestedVerticalScrollUnconsumed(coordinatorLayout, child, mScrollDirection, dyConsumed, mTotalDyUnconsumed);
} else if (dyUnconsumed < 0 && mTotalDyUnconsumed > 0) {
mTotalDyUnconsumed = 0;
mScrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN;
onNestedVerticalScrollUnconsumed(coordinatorLayout, child, mScrollDirection, dyConsumed, mTotalDyUnconsumed);
}
mTotalDyUnconsumed += dyUnconsumed;
if (dyConsumed > 0 && mTotalDyConsumed < 0) {
mTotalDyConsumed = 0;
mConsumedScrollDirection = ScrollDirection.SCROLL_DIRECTION_UP;
onNestedVerticalScrollConsumed(coordinatorLayout, child, mConsumedScrollDirection, dyConsumed, mTotalDyConsumed);
} else if (dyConsumed < 0 && mTotalDyConsumed > 0) {
mTotalDyConsumed = 0;
mConsumedScrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN;
onNestedVerticalScrollConsumed(coordinatorLayout, child, mConsumedScrollDirection, dyConsumed, mTotalDyConsumed);
}
mTotalDyConsumed += dyConsumed;
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
if (dy > 0 && mTotalDy < 0) {
mTotalDy = 0;
mPreScrollDirection = ScrollDirection.SCROLL_DIRECTION_UP;
onNestedVerticalPreScroll(coordinatorLayout, child, target, dx, dy, consumed, mPreScrollDirection);
} else if (dy < 0 && mTotalDy > 0) {
mTotalDy = 0;
mPreScrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN;
onNestedVerticalPreScroll(coordinatorLayout, child, target, dx, dy, consumed, mPreScrollDirection);
}
mTotalDy += dy;
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, boolean consumed) {
super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
return onNestedDirectionFling(coordinatorLayout, child, target, velocityX, velocityY, consumed
, velocityY > 0 ? ScrollDirection.SCROLL_DIRECTION_UP : ScrollDirection.SCROLL_DIRECTION_DOWN);
}
/**
* @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
* associated with
* @param child the child view of the CoordinatorLayout this Behavior is associated with
* @param scrollDirection Direction of the scroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN
* @param currentOverScroll Unconsumed value, negative or positive based on the direction;
* @param totalScroll Cumulative value for current direction (Unconsumed)
*/
public abstract void onNestedVerticalScrollUnconsumed(CoordinatorLayout coordinatorLayout, V child, @ScrollDirection int scrollDirection, int currentOverScroll, int totalScroll);
/**
* @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
* associated with
* @param child the child view of the CoordinatorLayout this Behavior is associated with
* @param scrollDirection Direction of the scroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN
* @param currentOverScroll Unconsumed value, negative or positive based on the direction;
* @param totalConsumedScroll Cumulative value for current direction (Unconsumed)
*/
public abstract void onNestedVerticalScrollConsumed(CoordinatorLayout coordinatorLayout, V child, @ScrollDirection int scrollDirection, int currentOverScroll, int totalConsumedScroll);
/**
* @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
* associated with
* @param child the child view of the CoordinatorLayout this Behavior is associated with
* @param target the descendant view of the CoordinatorLayout performing the nested scroll
* @param dx the raw horizontal number of pixels that the user attempted to scroll
* @param dy the raw vertical number of pixels that the user attempted to scroll
* @param consumed out parameter. consumed[0] should be set to the distance of dx that
* was consumed, consumed[1] should be set to the distance of dy that
* was consumed
* @param scrollDirection Direction of the scroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN
*/
public abstract void onNestedVerticalPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, @ScrollDirection int scrollDirection);
/**
* @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
* associated with
* @param child the child view of the CoordinatorLayout this Behavior is associated with
* @param target the descendant view of the CoordinatorLayout performing the nested scroll
* @param velocityX horizontal velocity of the attempted fling
* @param velocityY vertical velocity of the attempted fling
* @param consumed true if the nested child view consumed the fling
* @param scrollDirection Direction of the scroll: SCROLL_DIRECTION_UP, SCROLL_DIRECTION_DOWN
* @return true if the Behavior consumed the fling
*/
protected abstract boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, boolean consumed, @ScrollDirection int scrollDirection);
// @Override
// public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) {
// return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
// }
//
// @Override
// public WindowInsetsCompat onApplyWindowInsets(CoordinatorLayout coordinatorLayout, V child, WindowInsetsCompat insets) {
//
// return super.onApplyWindowInsets(coordinatorLayout, child, insets);
// }
//
// @Override
// public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
// return super.onSaveInstanceState(parent, child);
// }
}
public class BottomVerticalScrollBehavior<V extends View> extends VerticalScrollingBehavior<V> {
private static final Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator();
private int mBottomNavHeight;
private int mDefaultOffset;
// private WeakReference<V> mViewRef;
private ViewPropertyAnimatorCompat mTranslationAnimator;
private boolean hidden = false;
@Override
public boolean onLayoutChild(CoordinatorLayout parent, final V child, int layoutDirection) {
// First let the parent lay it out
parent.onLayoutChild(child, layoutDirection);
// mViewRef = new WeakReference<>(child);
child.post(new Runnable() {
@Override
public void run() {
mBottomNavHeight = child.getHeight();
}
});
mDefaultOffset = 0;
return super.onLayoutChild(parent, child, layoutDirection);
}
@Override
public void onNestedVerticalScrollUnconsumed(CoordinatorLayout coordinatorLayout, V child, @ScrollDirection int scrollDirection, int currentOverScroll, int totalScroll) {
// Empty body
}
@Override
public void onNestedVerticalPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, @ScrollDirection int scrollDirection) {
// handleDirection(child, scrollDirection);
}
@Override
public void onNestedVerticalScrollConsumed(CoordinatorLayout coordinatorLayout, V child, @ScrollDirection int scrollDirection, int currentOverScroll, int totalConsumedScroll) {
handleDirection(child, scrollDirection);
}
private void handleDirection(V child, int scrollDirection) {
if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_DOWN && hidden) {
hidden = false;
animateOffset(child, mDefaultOffset);
} else if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_UP && !hidden) {
hidden = true;
animateOffset(child, mBottomNavHeight + mDefaultOffset);
}
}
@Override
protected boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, boolean consumed, @ScrollDirection int scrollDirection) {
if (consumed) {
handleDirection(child, scrollDirection);
}
return consumed;
}
private void animateOffset(final V child, final int offset) {
ensureOrCancelAnimator(child);
mTranslationAnimator.translationY(offset).start();
}
private void ensureOrCancelAnimator(V child) {
if (mTranslationAnimator == null) {
mTranslationAnimator = ViewCompat.animate(child);
mTranslationAnimator.setDuration(400);
mTranslationAnimator.setInterpolator(INTERPOLATOR);
} else {
mTranslationAnimator.cancel();
}
}
}
public class BnbFollowListBehavior extends BottomVerticalScrollBehavior<FloatingActionButton> {
public BnbFollowListBehavior(Context context, AttributeSet attributeSet) {
super();
}
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<!--: elevation和pressedTranslationZ,分別為普通狀態下的陰影大小和點選時陰影的大小-->
<!--寬高包裹內容 百分百居中-->
<!--app:borderWidth="4dp" 不要這個 才有陰影-->
<android.support.design.widget.FloatingActionButton
android:layout_gravity="bottom|center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:backgroundTint="@color/colorAccent"
android:clickable="true"
android:focusable="true"
android:src="@drawable/appmanager"
app:borderWidth="4dp"
app:elevation="8dp"
app:fabSize="auto"
app:layout_behavior="com.as.recycler.BnbFollowListBehavior"
app:pressedTranslationZ="16dp"
app:rippleColor="@color/colorPrimaryDark"/>
</android.support.design.widget.CoordinatorLayout>
和上面一樣效果 程式碼少了很多
public class FabFollowListBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
private static final int MIN_DY = 30;
private static final String TAG = "FabFollowListBehavior";
public FabFollowListBehavior(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
/**
* 初始時不呼叫,滑動時呼叫---一次滑動過程,之呼叫一次
*/
@Override
public boolean onStartNestedScroll(
@NonNull CoordinatorLayout coordinatorLayout,
@NonNull FloatingActionButton child,
@NonNull View directTargetChild,
@NonNull View target, int axes, int type) {
return true;
}
/**
* @param dyConsumed 每次回撥前後的Y差值
*/
@Override
public void onNestedScroll(
@NonNull CoordinatorLayout coordinatorLayout,
@NonNull FloatingActionButton child,
@NonNull View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
//平移隱現
if (dyConsumed > MIN_DY) {//上滑:消失
showOrNot(coordinatorLayout, child, false).start();
} else if (dyConsumed < -MIN_DY) {//下滑滑:顯示
showOrNot(coordinatorLayout, child, true).start();
}
//僅滑動時消失
// if (dyConsumed > MIN_DY || dyConsumed < -MIN_DY) {//上滑:消失
// showOrNot(child).start();
// }
}
private Animator showOrNot(CoordinatorLayout coordinatorLayout, final View fab, boolean show) {
//獲取fab頭頂的高度
int hatHeight = coordinatorLayout.getBottom() - fab.getBottom() + fab.getHeight();
int end = show ? 0 : hatHeight;
float start = fab.getTranslationY();
ValueAnimator animator = ValueAnimator.ofFloat(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
fab.setTranslationY((Float) animation.getAnimatedValue());
}
});
return animator;
}
private Animator showOrNot(final View fab) {
//獲取fab頭頂的高度
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
fab.setScaleX((Float) animation.getAnimatedValue());
fab.setScaleY((Float) animation.getAnimatedValue());
}
});
return animator;
}
}
仔細看大佬給個 註釋的....滑動時再隱藏