详解Android使用CoordinatorLayout+AppBarLayout实现拉伸顶部图片功能
一、国际惯例,先看下效果图
二、不跟你多bb直接上布局文件代码
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:clipChildren="false" android:clipToPadding="false"> <androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_behavior="com.ce.myscrollimg.AppBarLayoutOverScrollViewBehavior"> <com.google.android.material.appbar.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_scrollFlags="scroll" android:clipChildren="false" android:clipToPadding="false"> <com.ce.myscrollimg.DisInterceptNestedScrollView android:layout_width="match_parent" android:layout_height="wrap_content" android:clipChildren="false" android:clipToPadding="false" app:layout_collapseMode="parallax" app:layout_collapseParallaxMultiplier="0.8"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/iv_bg" android:layout_width="match_parent" android:layout_height="130dp" android:src="@mipmap/ic_cover_1" android:scaleType="centerCrop"/> </LinearLayout> </com.ce.myscrollimg.DisInterceptNestedScrollView> <com.ce.myscrollimg.DisInterceptNestedScrollView android:layout_width="match_parent" android:layout_height="wrap_content" android:tag="middle" android:layout_marginTop="130dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <View android:layout_width="match_parent" android:layout_height="80dp" android:background="#FFF003"/> <View android:layout_width="match_parent" android:layout_height="80dp" android:background="#FF3300"/> </LinearLayout> </com.ce.myscrollimg.DisInterceptNestedScrollView> <androidx.appcompat.widget.Toolbar android:layout_width="match_parent" android:layout_height="wrap_content" android:tag="toolbar" app:layout_collapseMode="pin"> </androidx.appcompat.widget.Toolbar> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <com.google.android.material.tabs.TabLayout android:id="@+id/toolbar_tab" android:layout_width="match_parent" android:layout_height="40dp" android:layout_gravity="center" app:tabIndicatorColor="#FFC000" app:tabIndicatorFullWidth="false" app:tabIndicatorHeight="0dp" app:tabMode="scrollable" android:layout_marginTop="4dp" android:layout_marginBottom="2dp" app:tabSelectedTextColor="#FFC000" app:tabTextColor="#FFFFFF" app:tabMaxWidth="90dp" app:tabPaddingEnd="-1dp" /> <com.ce.myscrollimg.NoScrollViewPager android:id="@+id/vp_content" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout> </LinearLayout>
三、上java代码
package com.ce.myscrollimg; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.viewpager.widget.ViewPager; import android.graphics.Typeface; import android.os.Bundle; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import com.google.android.material.tabs.TabLayout; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private TabLayout toolbar_tab; private NoScrollViewPager vp_content; private ViewPagerAdapter vpAdapter; private List<Fragment> listFragment = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } //初始化view private void initView(){ //tab toolbar_tab = findViewById(R.id.toolbar_tab); // vp_content = findViewById(R.id.vp_content); vpAdapter = new ViewPagerAdapter(getSupportFragmentManager(),listFragment); vp_content.setAdapter(vpAdapter); vp_content.setOffscreenPageLimit(2); toolbar_tab.setupWithViewPager(vp_content); for(int i=0;i<12;i++){ listFragment.add(CeshiFragment.newInstance("第"+i+"页")); } vpAdapter.notifyDataSetChanged(); for(int i=0;i<listFragment.size();i++){ TabLayout.Tab tab = toolbar_tab.getTabAt(i); View customView = LayoutInflater.from(this).inflate(R.layout.tab_text, null, false); TextView textView = customView.findViewById(R.id.tv_custom_tab); // LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); // layoutParams.weight = 0; // textView.setLayoutParams(layoutParams); if (i == 0) { textView.setText("推荐"); } else { textView.setText("第"+i+"页"); } if (i == 0) { textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); textView.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); textView.setTextColor(getResources().getColor(R.color.color_FFFFC000)); } else { textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); textView.setTextColor(getResources().getColor(R.color._1E1E1E)); } tab.setCustomView(customView); } toolbar_tab.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { View view = tab.getCustomView(); if (view != null) { TextView textView = view.findViewById(R.id.tv_custom_tab); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); textView.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); textView.setTextColor(getResources().getColor(R.color.color_FFFFC000)); } vp_content.setCurrentItem(tab.getPosition()); } @Override public void onTabUnselected(TabLayout.Tab tab) { View view = tab.getCustomView(); if (view != null) { TextView textView = view.findViewById(R.id.tv_custom_tab); if (textView != null) { textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); textView.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL)); textView.setTextColor(getResources().getColor(R.color._1E1E1E)); } } } @Override public void onTabReselected(TabLayout.Tab tab) { } }); } }
四、重点在于设置AppBarLayout的Behavior这里自定义AppBarLayoutOverScrollViewBehavior,下面贴出代码
package com.ce.myscrollimg; import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import androidx.appcompat.widget.Toolbar; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.view.ViewCompat; import com.google.android.material.appbar.AppBarLayout; /** * Created by gjm on 2017/5/24. * 目前包括的事件: * 图片放大回弹 * 个人信息布局的top和botoom跟随图片位移 * toolbar背景变色 */ public class AppBarLayoutOverScrollViewBehavior extends AppBarLayout.Behavior { private static final String TAG = "overScroll"; private static final String TAG_TOOLBAR = "toolbar"; private static final String TAG_MIDDLE = "middle"; private static final float TARGET_HEIGHT = 1500; private View mTargetView; private int mParentHeight; private int mTargetViewHeight; private float mTotalDy; private float mLastScale; private int mLastBottom; private boolean isAnimate; private Toolbar mToolBar; private ViewGroup middleLayout;//个人信息布局 private int mMiddleHeight; private boolean isRecovering = false;//是否正在自动回弹中 private final float MAX_REFRESH_LIMIT = 0.3f;//达到这个下拉临界值就开始刷新动画 public AppBarLayoutOverScrollViewBehavior() { } public AppBarLayoutOverScrollViewBehavior(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, int layoutDirection) { boolean handled = super.onLayoutChild(parent, abl, layoutDirection); if (mToolBar == null) { mToolBar = parent.findViewWithTag(TAG_TOOLBAR); } if (middleLayout == null) { middleLayout = (ViewGroup) parent.findViewWithTag(TAG_MIDDLE); } // 需要在调用过super.onLayoutChild()方法之后获取 if (mTargetView == null) { mTargetView = parent.findViewById(R.id.iv_bg); if (mTargetView != null) { initial(abl); } } abl.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { @Override public final void onOffsetChanged(AppBarLayout appBarLayout, int i) { mToolBar.setAlpha(Float.valueOf(Math.abs(i)) / Float.valueOf(appBarLayout.getTotalScrollRange())); } }); return handled; } @Override public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes, int x) { isAnimate = true; if (target instanceof DisInterceptNestedScrollView) return true;//这个布局就是middleLayout return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes,x); } @Override public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int x) { if (!isRecovering) { if (mTargetView != null && ((dy < 0 && child.getBottom() >= mParentHeight) || (dy > 0 && child.getBottom() > mParentHeight))) { scale(child, target, dy); return; } } super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed,x); } @Override public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) { if (velocityY > 100) {//当y速度>100,就秒弹回 isAnimate = false; } return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); } @Override public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target, int x) { recovery(abl); super.onStopNestedScroll(coordinatorLayout, abl, target,x); } private void initial(AppBarLayout abl) { abl.setClipChildren(false); mParentHeight = abl.getHeight(); mTargetViewHeight = mTargetView.getHeight(); mMiddleHeight = middleLayout.getHeight(); } private void scale(AppBarLayout abl, View target, int dy) { mTotalDy += -dy; mTotalDy = Math.min(mTotalDy, TARGET_HEIGHT); mLastScale = Math.max(1f, 1f + mTotalDy / TARGET_HEIGHT); ViewCompat.setScaleX(mTargetView, mLastScale); ViewCompat.setScaleY(mTargetView, mLastScale); mLastBottom = mParentHeight + (int) (mTargetViewHeight / 2 * (mLastScale - 1)); abl.setBottom(mLastBottom); target.setScrollY(0); middleLayout.setTop(mLastBottom - mMiddleHeight); middleLayout.setBottom(mLastBottom); if (onProgressChangeListener != null) { float progress = Math.min((mLastScale - 1) / MAX_REFRESH_LIMIT, 1);//计算0~1的进度 onProgressChangeListener.onProgressChange(progress, false); } } public interface onProgressChangeListener { /** * 范围 0~1 * * @param progress * @param isRelease 是否是释放状态 */ void onProgressChange(float progress, boolean isRelease); } public void setOnProgressChangeListener(AppBarLayoutOverScrollViewBehavior.onProgressChangeListener onProgressChangeListener) { this.onProgressChangeListener = onProgressChangeListener; } onProgressChangeListener onProgressChangeListener; private void recovery(final AppBarLayout abl) { if (isRecovering) return; if (mTotalDy > 0) { isRecovering = true; mTotalDy = 0; if (isAnimate) { ValueAnimator anim = ValueAnimator.ofFloat(mLastScale, 1f).setDuration(200); anim.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (float) animation.getAnimatedValue(); ViewCompat.setScaleX(mTargetView, value); ViewCompat.setScaleY(mTargetView, value); abl.setBottom((int) (mLastBottom - (mLastBottom - mParentHeight) * animation.getAnimatedFraction())); middleLayout.setTop((int) (mLastBottom - (mLastBottom - mParentHeight) * animation.getAnimatedFraction() - mMiddleHeight)); if (onProgressChangeListener != null) { float progress = Math.min((value - 1) / MAX_REFRESH_LIMIT, 1);//计算0~1的进度 onProgressChangeListener.onProgressChange(progress, true); } } } ); anim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { isRecovering = false; } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); anim.start(); } else { ViewCompat.setScaleX(mTargetView, 1f); ViewCompat.setScaleY(mTargetView, 1f); abl.setBottom(mParentHeight); middleLayout.setTop(mParentHeight - mMiddleHeight); // middleLayout.setBottom(mParentHeight); isRecovering = false; if (onProgressChangeListener != null) onProgressChangeListener.onProgressChange(0, true); } } } }
五、源码下载
http://xiazai.jb51.net/201910/yuanma/MyScrollImg_jb51.rar
总结
以上所述是小编给大家介绍的Android使用CoordinatorLayout+AppBarLayout实现拉伸顶部图片功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
赞 (0)