欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-11-18 19:13:58
一、国际惯例,先看下效果图 二、不跟你多bb直接上布局文件代码 ...

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

详解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实现拉伸顶部图片功能,希望对大家有所帮助