服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - 详解Android使用CoordinatorLayout+AppBarLayout实现拉伸顶部图片功能

详解Android使用CoordinatorLayout+AppBarLayout实现拉伸顶部图片功能

2022-11-03 15:33程序员煊子 Android

这篇文章主要介绍了Android使用CoordinatorLayout+AppBarLayout实现拉伸顶部图片功能,本文实例文字相结合给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下

一、国际惯例,先看下效果图

详解Android使用CoordinatorLayout+AppBarLayout实现拉伸顶部图片功能

二、不跟你多bb直接上布局文件代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<?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代码

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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,下面贴出代码

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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);
   }
  }
 }
}

五、源码下载

MyScrollImg.rar

总结

以上所述是小编给大家介绍的Android使用CoordinatorLayout+AppBarLayout实现拉伸顶部图片功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

原文链接:https://blog.csdn.net/qq_35905005/article/details/102569502

延伸 · 阅读

精彩推荐