当前位置: 移动技术网 > IT编程>移动开发>Android > Android中FoldingLayout折叠布局的用法及实战全攻略

Android中FoldingLayout折叠布局的用法及实战全攻略

2019年07月24日  | 移动技术网IT编程  | 我要评论

女排半决赛时间,海归美女甘当无耻小三,张睿李晟吧

一、概述
无意中翻到的foldinglayout的介绍的博客,以及github地址。感觉很nice呀,于是花了点时间研究以及编写,本篇博客将带大家从最基本的原理分析,一步一步的实现我们的foldinglayout,当然了,如果你能力过硬,可以直接下载github上的代码进行学习。
博客基本分为以下几个部分:
1、matrix的setpolytopoly使用
2、在图片上使用渐变和阴影
3、初步的foldinglayout的实现,完成图片的折叠显示(可控制折叠次数、包含阴影的绘制)
4、引入手势,手指可以可以foldinglayout的折叠
5、结合drawerlayout实现折叠式侧滑
6、结合slidingpanelayout实现折叠式侧滑
ok,贴下部分的效果图:

2016229155451708.jpg (566×888)

改图对应上述3,妹子不错吧~

ok,对应上述4.

2016229155540595.gif (436×602)

对应上述5。
ok,挑选了部分图,不然太占篇幅了。
那么接下来,我们就按照顺序往下学习了~~~

二、matrix的setpolytopoly使用
想要实现折叠,最重要的就是其核心的原理了,那么第一步我们要了解的就是,如何能把一张正常显示的图片,让它能够进行偏移显示。
其实精髓就在于matrix的setpolytopoly的方法。

public boolean setpolytopoly(float[] src, int srcindex,  float[] dst, int dstindex,int pointcount)  
简单看一下该方法的参数,src代表变换前的坐标;dst代表变换后的坐标;从src到dst的变换,可以通过srcindex和dstindex来制定第一个变换的点,一般可能都设置位0。pointcount代表支持的转换坐标的点数,最多支持4个。
如果不明白没事,下面通过一个简单的例子,带大家了解:

package com.zhy.sample.folderlayout; 
 
import android.app.activity; 
import android.content.context; 
import android.graphics.bitmap; 
import android.graphics.bitmapfactory; 
import android.graphics.canvas; 
import android.graphics.matrix; 
import android.os.bundle; 
import android.view.view; 
 
public class matrixpolytopolyactivity extends activity 
{ 
 
  @override 
  protected void oncreate(bundle savedinstancestate) 
  { 
    super.oncreate(savedinstancestate); 
    setcontentview(new polytopolyview(this)); 
  } 
 
  class polytopolyview extends view 
  { 
 
    private bitmap mbitmap; 
    private matrix mmatrix; 
 
    public polytopolyview(context context) 
    { 
      super(context); 
      mbitmap = bitmapfactory.decoderesource(getresources(), 
          r.drawable.tanyan); 
      mmatrix = new matrix(); 
      float[] src = { 0, 0,// 
          mbitmap.getwidth(), 0,// 
          mbitmap.getwidth(), mbitmap.getheight(),// 
          0, mbitmap.getheight() }; 
      float[] dst = { 0, 0,// 
          mbitmap.getwidth(), 100,// 
          mbitmap.getwidth(), mbitmap.getheight() - 100,// 
          0, mbitmap.getheight() }; 
      mmatrix.setpolytopoly(src, 0, dst, 0, src.length >> 1); 
    } 
 
    @override 
    protected void ondraw(canvas canvas) 
    { 
      super.ondraw(canvas); 
      canvas.drawbitmap(mbitmap, mmatrix, null); 
    } 
 
  } 
 
} 

我们编写了一个polytopolyview作为我们的activity的主视图。
在polytopolyview中,我们加载了一张图片,初始化我们的matrix,注意src和dst两个数组,src就是正常情况下图片的4个顶点。dst将图片右侧两个点的y坐标做了些许的修改。
大家可以在纸上稍微标一下src和dst的四个点的位置。
最后我们在ondraw的时候进行图像的绘制,效果为:

2016229155640794.jpg (602×846)

如果你已经在纸上稍微的画了dst的四个点,那么这个结果你一定不陌生。
可以看到我们通过matrix.setpolytopoly实现了图片的倾斜,那么引入到折叠的情况,假设折叠两次,大家有思路么,考虑一下,没有的话,继续往下看。

三、引入阴影
其实阴影应该在实现初步的折叠以后来说,这样演示其实比较方便,但是为了降低其理解的简单性,我们先把阴影抽取出来说。
假设我们现在要给上图加上阴影,希望的效果图是这样的:

2016229155828122.jpg (558×840)

可以看到我们左侧加入了一点阴影,怎么实现呢?
主要还是利用lineargradient,我们从左到右添加一层从黑色到透明的渐变即可。

public class matrixpolytopolywithshadowactivity extends activity 
{ 
 
  @override 
  protected void oncreate(bundle savedinstancestate) 
  { 
    super.oncreate(savedinstancestate); 
    setcontentview(new polytopolyview(this)); 
 
  } 
 
  class polytopolyview extends view 
  { 
 
    private bitmap mbitmap; 
    private matrix mmatrix; 
     
    private paint mshadowpaint; 
    private matrix mshadowgradientmatrix; 
    private lineargradient mshadowgradientshader; 
 
    public polytopolyview(context context) 
    { 
      super(context); 
      mbitmap = bitmapfactory.decoderesource(getresources(), 
          r.drawable.tanyan); 
      mmatrix = new matrix(); 
 
      mshadowpaint = new paint(); 
      mshadowpaint.setstyle(style.fill); 
      mshadowgradientshader = new lineargradient(0, 0, 0.5f, 0, 
          color.black, color.transparent, tilemode.clamp); 
      mshadowpaint.setshader(mshadowgradientshader); 
 
      mshadowgradientmatrix = new matrix(); 
      mshadowgradientmatrix.setscale(mbitmap.getwidth(), 1); 
      mshadowgradientshader.setlocalmatrix(mshadowgradientmatrix); 
      mshadowpaint.setalpha((int) (0.9*255)); 
 
    } 
 
    @override 
    protected void ondraw(canvas canvas) 
    { 
      super.ondraw(canvas); 
      canvas.save(); 
      float[] src = //...; 
      float[] dst = //...; 
      mmatrix.setpolytopoly(src, 0, dst, 0, src.length >> 1); 
 
      canvas.concat(mmatrix); 
      canvas.drawbitmap(mbitmap, 0, 0, null); 
      //绘制阴影                                                            canvas.drawrect(0, 0, mbitmap.getwidth(), mbitmap.getheight(), 
          mshadowpaint); 
      canvas.restore(); 
 
    } 
 
  } 
 
} 

重点看mshadowpaint,mshadowgradientshader,mshadowgradientmatrix一个是画笔,我们为画笔设置了一个渐变的shader,这个shader的参数为
new lineargradient(0, 0, 0.5f, 0,color.black, color.transparent, tilemode.clamp);
起点(0,0)、终点(0.5f,0);颜色从和black到透明;模式为clamp,也就是拉伸最后一个像素。
这里你可能会问,这才为0.5个像素的区域设置了渐变,不对呀,恩,是的,继续看接下来我们使用了setlocalmatrix(mshadowgradientmatrix);,而这个
mshadowgradientmatrix将和坐标扩大了mbitmap.getwidth()倍,也就是说现在设置渐变的区域为(0.5f*mbitmap.getwidth(),0)半张图的大小,那么后半张图呢?
后半张应用clamp模式,拉伸的透明。
关于shader、setlocalmatrix等用法也可以参考:android bitmapshader 实战 实现圆形、圆角图片

四、初步实现折叠
了解了原理以及阴影的绘制以后,接下来要开始学习真正的去折叠了,我们的目标效果为:

2016229155856932.jpg (566×888)

妹子折叠成了8份,且阴影的范围为:每个沉下去夹缝的左右两侧,左侧黑色半透明遮盖,右侧短距离的黑色到透明阴影(大家可以仔细看)。
现在其实大家以及会将图片简单倾斜和添加阴影了,那么唯一的难点就是怎么将一张图分成很多快,我相信每块的折叠大家都会。
其实我们可以通过绘制该图多次,比如第一次绘制往下倾斜;第二次绘制网上倾斜;这样就和我们标题2的实现类似了,只需要利用setpolytopoly。
那么绘制多次,每次显示肯定不是一整张图,比如第一次,我只想显示第一块,所以我们还需要cliprect的配合,说到这,应该以及揭秘了~~~

package com.zhy.sample.folderlayout; 
 
import android.app.activity; 
import android.content.context; 
import android.graphics.bitmap; 
import android.graphics.bitmapfactory; 
import android.graphics.canvas; 
import android.graphics.color; 
import android.graphics.lineargradient; 
import android.graphics.matrix; 
import android.graphics.paint; 
import android.graphics.paint.style; 
import android.graphics.shader.tilemode; 
import android.os.bundle; 
import android.view.view; 
 
public class simpleuseactivity extends activity 
{ 
 
  @override 
  protected void oncreate(bundle savedinstancestate) 
  { 
    super.oncreate(savedinstancestate); 
    setcontentview(new polytopolyview(this)); 
 
  } 
 
  class polytopolyview extends view 
  { 
 
    private static final int num_of_point = 8; 
    /** 
     * 图片的折叠后的总宽度 
     */ 
    private int mtranslatedis; 
 
    /** 
     * 折叠后的总宽度与原图宽度的比例 
     */ 
    private float mfactor = 0.8f; 
    /** 
     * 折叠块的个数 
     */ 
    private int mnumoffolds = 8; 
 
    private matrix[] mmatrices = new matrix[mnumoffolds]; 
     
    private bitmap mbitmap; 
 
    /** 
     * 绘制黑色透明区域 
     */ 
    private paint msolidpaint; 
 
    /** 
     * 绘制阴影 
     */ 
    private paint mshadowpaint; 
    private matrix mshadowgradientmatrix; 
    private lineargradient mshadowgradientshader; 
 
    /*** 
     * 原图每块的宽度 
     */ 
    private int mflodwidth; 
    /** 
     * 折叠时,每块的宽度 
     */ 
    private int mtranslatedisperflod; 
 
    public polytopolyview(context context) 
    { 
      super(context); 
      mbitmap = bitmapfactory.decoderesource(getresources(), 
          r.drawable.tanyan); 
       
      //折叠后的总宽度 
      mtranslatedis = (int) (mbitmap.getwidth() * mfactor); 
      //原图每块的宽度 
      mflodwidth = mbitmap.getwidth() / mnumoffolds; 
      //折叠时,每块的宽度 
      mtranslatedisperflod = mtranslatedis / mnumoffolds; 
       
      //初始化matrix 
      for (int i = 0; i < mnumoffolds; i++) 
      { 
        mmatrices[i] = new matrix(); 
      } 
 
      msolidpaint = new paint(); 
      int alpha = (int) (255 * mfactor * 0.8f) ; 
      msolidpaint 
          .setcolor(color.argb((int) (alpha*0.8f), 0, 0, 0)); 
 
      mshadowpaint = new paint(); 
      mshadowpaint.setstyle(style.fill); 
      mshadowgradientshader = new lineargradient(0, 0, 0.5f, 0, 
          color.black, color.transparent, tilemode.clamp); 
      mshadowpaint.setshader(mshadowgradientshader); 
      mshadowgradientmatrix = new matrix(); 
      mshadowgradientmatrix.setscale(mflodwidth, 1); 
      mshadowgradientshader.setlocalmatrix(mshadowgradientmatrix); 
      mshadowpaint.setalpha(alpha); 
 
      //纵轴减小的那个高度,用勾股定理计算下 
      int depth = (int) math.sqrt(mflodwidth * mflodwidth 
          - mtranslatedisperflod * mtranslatedisperflod)/2; 
 
      //转换点 
      float[] src = new float[num_of_point]; 
      float[] dst = new float[num_of_point]; 
 
      /** 
       * 原图的每一块,对应折叠后的每一块,方向为左上、右上、右下、左下,大家在纸上自己画下 
       */ 
      for (int i = 0; i < mnumoffolds; i++) 
      { 
        src[0] = i * mflodwidth; 
        src[1] = 0; 
        src[2] = src[0] + mflodwidth; 
        src[3] = 0; 
        src[4] = src[2]; 
        src[5] = mbitmap.getheight(); 
        src[6] = src[0]; 
        src[7] = src[5]; 
 
        boolean iseven = i % 2 == 0; 
 
        dst[0] = i * mtranslatedisperflod; 
        dst[1] = iseven ? 0 : depth; 
        dst[2] = dst[0] + mtranslatedisperflod; 
        dst[3] = iseven ? depth : 0; 
        dst[4] = dst[2]; 
        dst[5] = iseven ? mbitmap.getheight() - depth : mbitmap 
            .getheight(); 
        dst[6] = dst[0]; 
        dst[7] = iseven ? mbitmap.getheight() : mbitmap.getheight() 
            - depth; 
 
        //setpolytopoly 
        mmatrices[i].setpolytopoly(src, 0, dst, 0, src.length >> 1); 
      } 
 
    } 
 
    @override 
    protected void ondraw(canvas canvas) 
    { 
      super.ondraw(canvas); 
      //绘制mnumoffolds次 
      for (int i = 0; i < mnumoffolds; i++) 
      { 
         
        canvas.save(); 
        //将matrix应用到canvas 
        canvas.concat(mmatrices[i]); 
        //控制显示的大小 
        canvas.cliprect(mflodwidth * i, 0, mflodwidth * i + mflodwidth, 
            mbitmap.getheight()); 
        //绘制图片 
        canvas.drawbitmap(mbitmap, 0, 0, null); 
        //移动绘制阴影 
        canvas.translate(mflodwidth * i, 0); 
        if (i % 2 == 0) 
        { 
          //绘制黑色遮盖 
          canvas.drawrect(0, 0, mflodwidth, mbitmap.getheight(), 
              msolidpaint); 
        }else 
        { 
          //绘制阴影 
          canvas.drawrect(0, 0, mflodwidth, mbitmap.getheight(), 
              mshadowpaint); 
        } 
        canvas.restore(); 
      } 
 
    } 
 
  } 
 
} 

简单讲解下,不去管绘制阴影的部分,其实折叠就是:
1、初始化转换点,这里注释说的很清楚,大家最好在纸上绘制下,标一下每个变量。
2、为matrix.setpolytopoly
3、绘制时使用该matrix,且cliprect控制显示区域(这个区域也很简单,原图的第一块到最后一块),最好就是绘制bitmap了。
阴影这里大家可以换个明亮点的图片去看看~~

五、foldlayout的实现
1、实现
我们的想法是这样的,我们的foldlayout只能有一个直接子元素,当然这个子元素可以是relativelayout什么的,可以很复杂。然后只要外层套了我们的foldlayout,就能实现折叠效果。
那么也就是说,我们的foldlayout折叠效果展示的是它的子元素的“样子”,那么如何或者这个“样子”呢?
大家都知道,我们的viewgroup有个方法叫做:dispatchdraw(canvas)主要用来绘制子元素,我们可以对这个canvas进行设置matrix,以及重复调用dispatchdraw(canvas)来实现类似上篇博客最后的效果,这样就完成了我们的可行性的分析。

package com.zhy.view; 
 
import android.content.context; 
import android.graphics.bitmap; 
import android.graphics.bitmap.config; 
import android.graphics.canvas; 
import android.graphics.color; 
import android.graphics.lineargradient; 
import android.graphics.matrix; 
import android.graphics.paint; 
import android.graphics.paint.style; 
import android.graphics.shader.tilemode; 
import android.util.attributeset; 
import android.view.view; 
import android.view.viewgroup; 
 
public class foldlayout extends viewgroup 
{ 
 
  private static final int num_of_point = 8; 
  /** 
   * 图片的折叠后的总宽度 
   */ 
  private float mtranslatedis; 
 
  protected float mfactor = 0.6f; 
 
  private int mnumoffolds = 8; 
 
  private matrix[] mmatrices = new matrix[mnumoffolds]; 
 
  private paint msolidpaint; 
 
  private paint mshadowpaint; 
  private matrix mshadowgradientmatrix; 
  private lineargradient mshadowgradientshader; 
 
  private float mflodwidth; 
  private float mtranslatedisperflod; 
 
  public foldlayout(context context) 
  { 
    this(context, null); 
  } 
 
  public foldlayout(context context, attributeset attrs) 
  { 
    super(context, attrs); 
 
    for (int i = 0; i < mnumoffolds; i++) 
    { 
      mmatrices[i] = new matrix(); 
    } 
 
    msolidpaint = new paint(); 
    mshadowpaint = new paint(); 
    mshadowpaint.setstyle(style.fill); 
    mshadowgradientshader = new lineargradient(0, 0, 0.5f, 0, color.black, 
        color.transparent, tilemode.clamp); 
    mshadowpaint.setshader(mshadowgradientshader); 
    mshadowgradientmatrix = new matrix(); 
    this.setwillnotdraw(false); 
 
  } 
 
  @override 
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) 
  { 
    view child = getchildat(0); 
    measurechild(child, widthmeasurespec, heightmeasurespec); 
    setmeasureddimension(child.getmeasuredwidth(), 
        child.getmeasuredheight()); 
 
  } 
 
  @override 
  protected void onlayout(boolean changed, int l, int t, int r, int b) 
  { 
    view child = getchildat(0); 
    child.layout(0, 0, child.getmeasuredwidth(), child.getmeasuredheight()); 
 
    mbitmap = bitmap.createbitmap(getmeasuredwidth(), getmeasuredheight(), 
        config.argb_8888); 
    mcanvas.setbitmap(mbitmap); 
    updatefold(); 
 
  } 
 
  private void updatefold() 
  { 
    int w = getmeasuredwidth(); 
    int h = getmeasuredheight(); 
 
    mtranslatedis = w * mfactor; 
    mflodwidth = w / mnumoffolds; 
    mtranslatedisperflod = mtranslatedis / mnumoffolds; 
 
    int alpha = (int) (255 * (1 - mfactor)); 
    msolidpaint.setcolor(color.argb((int) (alpha * 0.8f), 0, 0, 0)); 
 
    mshadowgradientmatrix.setscale(mflodwidth, 1); 
    mshadowgradientshader.setlocalmatrix(mshadowgradientmatrix); 
    mshadowpaint.setalpha(alpha); 
 
    float depth = (float) (math.sqrt(mflodwidth * mflodwidth 
        - mtranslatedisperflod * mtranslatedisperflod) / 2); 
 
    float[] src = new float[num_of_point]; 
    float[] dst = new float[num_of_point]; 
 
    for (int i = 0; i < mnumoffolds; i++) 
    { 
      mmatrices[i].reset(); 
      src[0] = i * mflodwidth; 
      src[1] = 0; 
      src[2] = src[0] + mflodwidth; 
      src[3] = 0; 
      src[4] = src[2]; 
      src[5] = h; 
      src[6] = src[0]; 
      src[7] = src[5]; 
 
      boolean iseven = i % 2 == 0; 
 
      dst[0] = i * mtranslatedisperflod; 
      dst[1] = iseven ? 0 : depth; 
 
      dst[2] = dst[0] + mtranslatedisperflod; 
      dst[3] = iseven ? depth : 0; 
      dst[4] = dst[2]; 
      dst[5] = iseven ? h - depth : h; 
      dst[6] = dst[0]; 
      dst[7] = iseven ? h : h - depth; 
 
      for (int y = 0; y < 8; y++) 
      { 
        dst[y] = math.round(dst[y]); 
      } 
 
      mmatrices[i].setpolytopoly(src, 0, dst, 0, src.length >> 1); 
    } 
  } 
 
  private canvas mcanvas = new canvas(); 
  private bitmap mbitmap; 
  private boolean isready; 
 
  @override 
  protected void dispatchdraw(canvas canvas) 
  { 
 
    if (mfactor == 0) 
      return; 
    if (mfactor == 1) 
    { 
      super.dispatchdraw(canvas); 
      return; 
    } 
    for (int i = 0; i < mnumoffolds; i++) 
    { 
      canvas.save(); 
 
      canvas.concat(mmatrices[i]); 
      canvas.cliprect(mflodwidth * i, 0, mflodwidth * i + mflodwidth, 
          getheight()); 
      if (isready) 
      { 
        canvas.drawbitmap(mbitmap, 0, 0, null); 
      } else 
      { 
        // super.dispatchdraw(canvas); 
        super.dispatchdraw(mcanvas); 
        canvas.drawbitmap(mbitmap, 0, 0, null); 
        isready = true; 
      } 
      canvas.translate(mflodwidth * i, 0); 
      if (i % 2 == 0) 
      { 
        canvas.drawrect(0, 0, mflodwidth, getheight(), msolidpaint); 
      } else 
      { 
        canvas.drawrect(0, 0, mflodwidth, getheight(), mshadowpaint); 
      } 
      canvas.restore(); 
    } 
  } 
    //...dispatchdraw 
 
  public void setfactor(float factor) 
  { 
    this.mfactor = factor; 
    updatefold(); 
    invalidate(); 
  } 
 
  public float getfactor() 
  { 
    return mfactor; 
  } 
 
} 

上述代码大家应该不陌生,只是把从view对单个图片进行绘制的修改为了viewgroup。
既然是viewgroup少不了onmeasure,onlayout等。测量和布局完全依赖于它的子view。
然后将需要初始化的一些东西,不依赖于宽度的,比如画笔什么的都放在构造中;依赖宽高的,都在onlayout之后,调用了updatefold();进行初始化相关代码。
updatefold中的代码,我们也不陌生,因为和上篇博客基本一致。主要就是计算mflodwidth,mtranslatedisperflod以及根据设置的mnumoffolds去循环初始化我们的matrix.
matrix完成setpolytopoly以后,我们就可以去绘制了:

private canvas mcanvas = new canvas(); 
  private bitmap mbitmap; 
  private boolean isready; 
 
  @override 
  protected void dispatchdraw(canvas canvas) 
  { 
 
    if (mfactor == 0) 
      return; 
    if (mfactor == 1) 
    { 
      super.dispatchdraw(canvas); 
      return; 
    } 
    for (int i = 0; i < mnumoffolds; i++) 
    { 
      canvas.save(); 
 
      canvas.concat(mmatrices[i]); 
      canvas.cliprect(mflodwidth * i, 0, mflodwidth * i + mflodwidth, 
          getheight()); 
      if (isready) 
      { 
        canvas.drawbitmap(mbitmap, 0, 0, null); 
      } else 
      { 
        // super.dispatchdraw(canvas); 
        super.dispatchdraw(mcanvas); 
        canvas.drawbitmap(mbitmap, 0, 0, null); 
        isready = true; 
      } 
      canvas.translate(mflodwidth * i, 0); 
      if (i % 2 == 0) 
      { 
        canvas.drawrect(0, 0, mflodwidth, getheight(), msolidpaint); 
      } else 
      { 
        canvas.drawrect(0, 0, mflodwidth, getheight(), mshadowpaint); 
      } 
      canvas.restore(); 
    } 
  } 

mfactor主要代表折叠后的总宽度与原宽度的比值,默认不折叠时为1,所以直接调用super.dispatchdraw(canvas);
那么如果为0,说明全部折起来了,我们直接if (mfactor == 0)return;就不要绘制了。
如果(0,1)之间就是正常情况了,如果还记得上一篇博客内容,无非就是根据mnumoffolds循环绘制多次,每次绘制的时候设置matrix,利用cliprect就可以实现我们的折叠。
这里大家注意看,我在第一次绘制的时候,调用了:

super.dispatchdraw(mcanvas); 
canvas.drawbitmap(mbitmap, 0, 0, null); 
isready = true; 

在我们自己new的mbitmap中也绘制了一份图片,因为我不希望每次都是调用super.dispatchdraw,所以只要isready=true,我们就可以去调用绘制mbitmap而避免调用super.dispatchdraw()。
绘制完成图片,就是绘制黑色的遮盖和阴影了~~,就是两个rect的绘制。
完成这些以后,我们可以简单的坐下测试,使用我们的布局。

2、测试
布局文件:

<com.zhy.view.foldlayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:id="@+id/id_fold_layout" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" > 
 
  <imageview 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:scaletype="fitxy" 
    android:src="@drawable/xueshan" /> 
 
</com.zhy.view.foldlayout> 

activity

package com.zhy.sample.folderlayout; 
 
import com.zhy.view.foldlayout; 
 
import android.animation.objectanimator; 
import android.annotation.suppresslint; 
import android.app.activity; 
import android.os.bundle; 
 
public class foldlayoutactivity extends activity 
{ 
  private foldlayout mfoldlayout; 
 
  @override 
  protected void oncreate(bundle savedinstancestate) 
  { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.activity_fold); 
 
    mfoldlayout = (foldlayout) findviewbyid(r.id.id_fold_layout); 
   
    /*mfoldlayout.post(new runnable() 
    { 
 
      @suppresslint("newapi") 
      @override 
      public void run() 
      { 
        objectanimator.offloat(mfoldlayout, "factor", 1, 0, 1) 
            .setduration(5000).start(); 
      } 
    });*/ 
 
  } 
} 

现在的效果是,我们把mfactor改为0.6f:

2016229160025655.jpg (754×1244)

当然了,此时只是静态的,但是我们成功的完成了绘制一个静态图到flodlayout。
接下来我们为其增加手指的触摸折叠功能。

六、touchfoldlayout
1、实现
增加触摸功能其实很简单,我们的绘制依赖mfactor这个值,我们只要在ontouchevent里面去累加手指移动距离,然后动态更新这个值就可以了。

package com.zhy.view; 
 
import android.content.context; 
import android.graphics.canvas; 
import android.util.attributeset; 
import android.view.gesturedetector; 
import android.view.motionevent; 
 
public class touchfoldlayout extends foldlayout 
{ 
 
  private gesturedetector mscrollgesturedetector; 
 
  public touchfoldlayout(context context, attributeset attrs) 
  { 
    super(context, attrs); 
    init(context, attrs); 
  } 
 
  public void init(context context, attributeset attrs) 
  { 
    mscrollgesturedetector = new gesturedetector(context, 
        new scrollgesturedetector()); 
 
  } 
 
  @override 
  public boolean ontouchevent(motionevent event) 
  { 
    return mscrollgesturedetector.ontouchevent(event); 
  } 
 
  private int mtranslation = -1; 
 
  @override 
  protected void dispatchdraw(canvas canvas) 
  { 
    if (mtranslation == -1) 
      mtranslation = getwidth(); 
    super.dispatchdraw(canvas); 
  } 
 
  class scrollgesturedetector extends gesturedetector.simpleongesturelistener 
  { 
    @override 
    public boolean ondown(motionevent e) 
    { 
      return true; 
    } 
 
    @override 
    public boolean onscroll(motionevent e1, motionevent e2, 
        float distancex, float distancey) 
    { 
      mtranslation -= distancex; 
 
      if (mtranslation < 0) 
      { 
        mtranslation = 0; 
      } 
      if (mtranslation > getwidth()) 
      { 
        mtranslation = getwidth(); 
      } 
 
      float factor = math.abs(((float) mtranslation) 
          / ((float) getwidth())); 
 
      setfactor(factor); 
 
      return true; 
    } 
  } 
 
} 

我们选择继承foldlayout,重写其ontouchevent,然后通过mscrollgesturedetector获取移动的距离,最终和width做比值得到我们的factor,然后调用setfactor进行改变。

public void setfactor(float factor) 
{ 
  this.mfactor = factor; 
  updatefold(); 
  invalidate(); 
} 

ok,这样就完成了引入手指的控制。

2、测试
现在改变下布局文件里面的类:

<com.zhy.view.touchfoldlayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:id="@+id/id_fold_layout" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" > 
 
  <imageview 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:scaletype="fitxy" 
    android:src="@drawable/xueshan" /> 
 
</com.zhy.view.touchfoldlayout> 

activity不变,看一下测试效果:(测试前记得把mfactor改为默认值1.0f)

至此我们完成了炫酷的效果,但是我们还需要应用到具体的案例上,否则就是特技,有必要duang一下。
于是我们首先考虑增加到侧滑菜单中去,侧滑菜单有很多选择,google也提供了两个,一个是drawerlayout,另一个是slidingpanelayout。
下面分别展示如何整合入这两个布局。
首先看slidingpanelayout,因为drawerlayout还有些地方需要修改。

八、foldslidingpanellayout

1、实现
对于slidingpanelayout的使用,应该没什么问题吧,就是把布局文件的根布局设置为slidingpanelayout,然后里面放两个子布局,一个代表侧滑菜单,一个代表内容区域。
那么,我们怎么整合到slidingpanelayout种去呢?大致两种方法:
1、把我们的折叠菜单作为侧滑布局的根布局,然后在activity种去监听setpanelslidelistener做出改变。
2、直接继承slidingpanelayout,再其内部将child(0)用foldlayout包起来,然后监听setpanelslidelistener。
这里我们选择后者,因为后者封装好了,就能直接按照slidingpanelayout原本的方式去使用了,不需要做多余的操作。
下面看代码:

package com.zhy.view; 
 
import android.content.context; 
import android.support.v4.widget.slidingpanelayout; 
import android.util.attributeset; 
import android.view.view; 
import android.view.viewgroup; 
 
public class foldslidingpanellayout extends slidingpanelayout 
{ 
  public foldslidingpanellayout(context context, attributeset attrs) 
  { 
    super(context, attrs); 
  } 
  @override 
  protected void onattachedtowindow() 
  { 
    super.onattachedtowindow(); 
     
    view child = getchildat(0); 
    if (child != null) { 
 
      removeview(child); 
      final foldlayout foldlayout = new foldlayout(getcontext()); 
      //foldlayout.setanchor(0); 
      foldlayout.addview(child); 
      viewgroup.layoutparams laypar = child.getlayoutparams(); 
      addview(foldlayout, 0, laypar); 
       
      setpanelslidelistener(new panelslidelistener() 
      { 
         
        @override 
        public void onpanelslide(view arg0, float arg1) 
        { 
          foldlayout.setfactor(arg1); 
        } 
         
        @override 
        public void onpanelopened(view arg0) 
        { 
          // todo auto-generated method stub 
           
        } 
         
        @override 
        public void onpanelclosed(view arg0) 
        { 
           
        } 
      }); 
       
    } 
  } 
} 

我们继承了slidingpanelayout,然后在onattachedtowindow中,取出侧滑的布局,在外层包上一个foldlayout;并且在内部去监听setpanelslidelistener,在onpanelslide种根据参数,去动态设置foldlayout的factor.
2、测试
(1)、布局文件

<com.zhy.view.foldslidingpanellayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:id="@+id/id_drawerlayout" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" > 
 
  <fragment 
    android:id="@+id/id_left_menu" 
    android:name="com.zhy.sample.folderlayout.leftmenufragment" 
    android:layout_width="240dp" 
    android:layout_height="match_parent" /> 
 
  <relativelayout 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" > 
 
    <imageview 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      android:scaletype="fitcenter" 
      android:src="@drawable/xueshan" /> 
  </relativelayout> 
 
</com.zhy.view.foldslidingpanellayout> 

我们的菜单使用的是一个fragment。
(2)、菜单布局

package com.zhy.sample.folderlayout; 
 
import android.os.bundle; 
import android.support.v4.app.fragment; 
import android.view.layoutinflater; 
import android.view.view; 
import android.view.viewgroup; 
import android.widget.arrayadapter; 
import android.widget.listview; 
 
public class leftmenufragment extends fragment 
{ 
  private listview mmenus; 
  private string[] mmenuitemstr = { "bear", "bird", "cat", "tigers", "panda" }; 
 
  @override 
  public view oncreateview(layoutinflater inflater, viewgroup container, 
      bundle savedinstancestate) 
  { 
 
    view view = inflater.inflate(r.layout.fragment_left_menu, container, 
        false); 
    mmenus = (listview) view.findviewbyid(r.id.id_left_menu_lv); 
    mmenus.setadapter(new arrayadapter<string>(getactivity(), 
        r.layout.item_left_menu, mmenuitemstr)); 
    return view; 
  } 
} 

<listview xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:id="@+id/id_left_menu_lv" 
  android:layout_width="240dp" 
  android:layout_height="match_parent" 
  android:layout_gravity="start" 
  android:background="#fff" 
  android:choicemode="singlechoice" /> 

item就是一个textview,就不贴了~~
3、activity

package com.zhy.sample.folderlayout; 
 
import android.os.bundle; 
import android.support.v4.app.fragmentactivity; 
 
public class slidingpanellayoutsampleactivity extends fragmentactivity 
{ 
  @override 
  protected void oncreate(bundle arg0) 
  { 
    super.oncreate(arg0); 
    setcontentview(r.layout.activity_slidingpanellayout); 
  } 
} 

恩,activity里面什么都不用做,引入布局文件就行了。
最好看看效果图。
3、效果图

这里菜单块数比较多,大家可以自行修改运行。
ok,到此我们将foldlayout与slidingpanelayout进行了整合,构造了这么个个性的侧滑。
最好还剩下与drawerlayout的整合。

九、folddrawerlayout
1、实现
关于drawerlayout的使用,与上面的slidingpanelayout类似,写写布局文件,引入activity就好了。我们依然使用上述的方法2,去实现一个drawerlayout的子类。

package com.zhy.view; 
 
import android.content.context; 
import android.support.v4.view.gravitycompat; 
import android.support.v4.view.viewcompat; 
import android.support.v4.widget.drawerlayout; 
import android.util.attributeset; 
import android.util.log; 
import android.view.gravity; 
import android.view.view; 
import android.view.viewgroup; 
 
public class folddrawerlayout extends drawerlayout 
{ 
  private static final string tag = "drawerfoldlayout"; 
 
  public folddrawerlayout(context context, attributeset attrs) 
  { 
    super(context, attrs); 
  } 
 
  @override 
  protected void onattachedtowindow() 
  { 
    super.onattachedtowindow(); 
 
    final int childcount = getchildcount(); 
    for (int i = 0; i < childcount; i++) 
    { 
      final view child = getchildat(i); 
      if (isdrawerview2(child)) 
      { 
        log.e(tag, "at" + i); 
        foldlayout foldlayout = new foldlayout( 
            getcontext()); 
        //<span style="font-family: arial, helvetica, sans-serif;">foldlayout</span><span style="font-family: arial, helvetica, sans-serif;">.setanchor(1);</span> 
        removeview(child); 
        foldlayout.addview(child); 
        viewgroup.layoutparams laypar = child.getlayoutparams(); 
        addview(foldlayout, i, laypar); 
      } 
 
    } 
    setdrawerlistener(new drawerlistener() 
    { 
 
      @override 
      public void ondrawerstatechanged(int arg0) 
      { 
        // todo auto-generated method stub 
 
      } 
 
      @override 
      public void ondrawerslide(view drawerview, float slideoffset) 
      { 
 
        if (drawerview instanceof foldlayout) 
        { 
          foldlayout foldlayout = ((foldlayout) drawerview); 
          log.e(tag, "slideoffset = " + slideoffset); 
          foldlayout.setfactor(slideoffset); 
        } 
 
      } 
 
      @override 
      public void ondraweropened(view arg0) 
      { 
 
      } 
 
      @override 
      public void ondrawerclosed(view arg0) 
      { 
 
      } 
    }); 
 
  } 
 
  boolean isdrawerview2(view child) 
  { 
    final int gravity = ((layoutparams) child.getlayoutparams()).gravity; 
    final int absgravity = gravitycompat.getabsolutegravity(gravity, 
        viewcompat.getlayoutdirection(child)); 
    return (absgravity & (gravity.left | gravity.right)) != 0; 
  } 
 
} 

看到这,大家可能会想,然后就和slidingpanelayout一样,写写布局文件就好了?其实不是的,如果你这么做了,你会发现侧滑很难拉出来,因为是这样的:
drawelayout的侧滑菜单,比如我们拉出来50%,那么正常来说显示的时侧滑布局右侧的50%,但是这个0.5如果设置给我们的factor,它会把布局缩小到50%且在左边。
导致,你拉了50%其实还是上面都看不到,因为折叠到左侧的50%去了。这里依然有两种解决方案:
(1)、结合属性动画,做偏移,具体可参考:android drawerlayout 高仿qq5.2双向侧滑菜单
(2)、让我们的折叠,收缩到最终的位置可以控制,我们现在统统往最坐标收缩,如果可以设置为最右边,那么本例就没有问题了。
2、引入anchor
我们引入一个manchor变量,值范围[0,1],控制foldlayout最终折叠到的位置。其实修改的代码比较少,我贴一下修改的代码:

private void updatefold() 
  { 
     
    //... 
    float anchorpoint = manchor * w; 
    float midfold = (anchorpoint / mflodwidth); 
 
    for (int i = 0; i < mnumoffolds; i++) 
    { 
      //引入anchor 
      dst[0] = (anchorpoint > i * mflodwidth) ? anchorpoint 
          + (i - midfold) * mtranslatedisperflod : anchorpoint 
          - (midfold - i) * mtranslatedisperflod; 
      dst[2] = (anchorpoint > (i + 1) * mflodwidth) ? anchorpoint 
          + (i + 1 - midfold) * mtranslatedisperflod : anchorpoint 
          - (midfold - i - 1) * mtranslatedisperflod;                               }         

                                                                                                     } 
             

唯一改变的就是dst[0]和dst[2]的坐标,当然了,anchor引入以后,你需要判断原始的坐标是否小于anchorpoint,如果小于需要加一些偏移量,大于则反之。
记得:

public void setanchor(float anchor) 
  { 
    this.manchor = anchor; 
    updatefold(); 
    invalidate(); 
  } 

打开上述的folddrawerlayout的这行代码:foldlayout.setanchor(1);让其最后合并位置为右侧。
使用方式,现在就是写好布局文件,大家直接使用slidingpanelayout那个布局文件,改一个根布局类就行。
3、效果图

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网