详解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)

相关推荐

  • Android中关于自定义相机预览界面拉伸问题

    关于自定义相机预览界面拉伸问题 1.导致主要变形的原因是Camera预览界面旋转的角度和摄像头挂载的角度不同导致的 2.我们的Activity设置的方向是竖屏,这是手机的自然方向 所以宽比高短 3.角度:所谓屏幕和摄像头的角度,指的是相对于自然方向旋转过的角度,根据旋转角度即可获知当前的方向 4.假如说:手机是竖屏的情况下, 自然角度为0,但是Camera逆时针旋转90度,那咱们设置顺时针旋转90度,就正常 .手机是横屏的情况下Camera返回为0度 ,如果设置顺时针旋转90度,就回旋转 怎么设

  • Android中ImageView.src设置图片拉伸、填满控件的方法

    问题 ImageView.src设置图片资源,图片不拉伸了,却有空隙部分: <LinearLayout android:id="@+id/linearLayout1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > <ImageView andro

  • Android开发框架之自定义ZXing二维码扫描界面并解决取景框拉伸问题

    先给大家展示下效果图: 扫描内容是下面这张,二维码是用zxing库生成的 由于改了好几个类,还是去年的事都忘得差不多了,所以只能上这个类的代码了,主要就是改了这个CaptureActivity.java package com.zxing.activity; import java.io.IOException; import java.util.Vector; import android.app.Activity; import android.content.Intent; import

  • Android Zxing二维码扫描图片拉伸的解决方法

    二维码扫描,Android Zxing图片拉伸解决. Zxing是google提供的二维码扫描工程 默认是横屏的  转换成竖屏后图片出现拉伸 这里提供解决办法: Zxing 修改 CameraConfigurationManager.Java文件的void initFromCameraParameters(Camera camera)方法 在Log.d(TAG, "Screen resolution: " + screenResolution);这句之后增加 Point screenR

  • Android 仿高德地图可拉伸的BottomSheet的示例代码

    前言 最近项目中需要用到高德地图搜索结果后的结果展示的可拉伸控件. gaode.gif 而我看到这个效果图,觉得这个就是一个slidingpanel,但是翻阅了一些发现用google自带的bottomsheet实现更方便 什么是BottomSheet? Bottom Sheet是Design Support Library23.2 版本引入的一个类似于对话框的控件,可以暂且叫做底部弹出框吧. Bottom Sheet中的内容默认是隐藏起来的,只显示很小一部分,可以通过在代码中设置其状态或者手势操

  • Android Zxing二维码扫描图片拉伸问题的解决方法

    还是这个接手项目,二维码扫描集成的是zxing,扫描界面的图像有明显的拉伸变形. 这种问题,根据以往的经验,一般是x,y轴错位引起的,处理好x,y轴的问题,一般可以解决问题. 由于这个问题,之前有很多人遇到,并分享在网上了,所以,我这里也就不需要重复造轮子了. 这里看了一篇博客:Android Zxing二维码扫描图片拉伸,用了上面的办法, 成功的解决图片拉伸问题. 解决方法如下: 修改CameraConfigurationManager.Java里面的initFromCameraParamet

  • 详解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:tool

  • 详解Android使用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayou实现手指滑动效果

    CoordinatorLayout+AppBarLayout+CollapsingToolbarLayou实现手指滑动效果 如何使用 CoordinatorLayout+AppBarLayout+CollapsingToolbarLayou实现下面GIF图中的效果,再展开的时候头像处于红白中间,根据收缩程度改变头像的位置!底下的RecyclerView也跟随这个移动,不会出现中间隔出一段距离!(仅提供源码复制粘贴,很简单的) 先看下效果图: 下面上代码 XML布局代码如下: <?xml vers

  • 详解Android TableLayout中stretchColumns、shrinkColumns的用法

    详解Android 中TableLayout中stretchColumns.shrinkColumns的用法 android:stretchColumns="1" android:shrinkColumns="1"这两个属性是TableLayout所特有的,也是这两个属性影响了子对象的布局. 表格布局是按照行列来组织子视图的布局.表格布局包含一系列的Tabrow对象,用于定义行(也可以使用其它子对象).表格布局不为它的行.列和单元格显示表格线.每个行可以包含个以上(

  • 详解Android内存优化策略

    目录 前言 一.内存优化策略 二.具体优化的点 1.避免内存泄漏 2.Bitmap等大对象的优化策略 (1) 优化Bitmap分辨率 (2) 优化单个像素点内存 (3) Bitmap的缓存策略 (4) drawable资源选择合适的drawable文件夹存放 (5) 其他大对象的优化 (6) 避免内存抖动 3.原生API回调释放内存 4.内存排查工具 (1)LeakCanary监测内存泄漏 (2)通过Proflier监控内存 (3)通过MAT工具排查内存泄漏 总结 前言 在开始之前需要先搞明白一

  • 详解Android中Intent对象与Intent Filter过滤匹配过程

    如果对Intent不是特别了解,可以参见博文<详解Android中Intent的使用方法>,该文对本文要使用的action.category以及data都进行了详细介绍.如果想了解在开发中常见Intent的使用,可以参见<Android中Intent习惯用法>. 本文内容有点长,希望大家可以耐心读完. 本文在描述组件在manifest中注册的Intent Filter过滤器时,统一用intent-filter表示. 一.概述 我们知道,Intent是分两种的:显式Intent和隐式

  • 详解Android Webview加载网页时发送HTTP头信息

    详解Android Webview加载网页时发送HTTP头信息 当你点击一个超链接进行跳转时,WebView会自动将当前地址作为Referer(引荐)发给服务器,因此很多服务器端程序通过是否包含referer来控制盗链,所以有些时候,直接输入一个网络地址,可能有问题,那么怎么解决盗链控制问题呢,其实在webview加载时加入一个referer就可以了,如何添加呢? 从Android 2.2 (也就是API 8)开始,WebView新增加了一个接口方法,就是为了便于我们加载网页时又想发送其他的HT

  • 详解Android获得系统GPU参数 gl.glGetString

    详解Android获得系统GPU参数 gl.glGetString 通过文档的查找,以及源码的剖析,Android的GPU信息需要通过OpenGL来获取,android framework层提供GL10来获取相应的参数,而GL10要在使用自定义的View时才可以获得,下面是获得GPU信息的例子: 1.实现Render类 class DemoRenderer implements GLSurfaceView.Renderer { public void onSurfaceCreated(GL10

  • 详解Android通过修改配置文件设置wifi密码

    详解Android通过修改配置文件设置wifi密码 前言: 在一些非常规Android设备上,如眼镜/手表,输入wifi密码如同一场灾难.此时可以通过修改配置文件的方法设置wifi的ssid和密码. wifi密码配置文件 首先要保证设备已经root,wifi的配置文件在/data/misc/wifi/wpa_supplicant.conf,可以先将其pull出来,然后在下面加上network开头的那部分就ok了.然后再导入进去.下面是我的配置文件: ##### wpa_supplicant co

  • 详解Android更改APP语言模式的实现过程

    一.效果图 二.描述 更改Android项目中的语言,这个作用于只用于此APP,不会作用于整个系统 三.解决方案 (一)布局文件 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" a

  • 详解android 通过uri获取bitmap图片并压缩

    详解android 通过uri获取bitmap图片并压缩 很多人在调用图库选择图片时会在onActivityResult中用Media.getBitmap来获取返回的图片,如下: Uri mImageCaptureUri = data.getData(); Bitmap photoBmp = null; if (mImageCaptureUri != null) { photoBmp = MediaStore.Images.Media.getBitmap(ac.getContentResolve

随机推荐