您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
Android FoldingLayout 折叠布局 原理及实现(一)
 
作者:张鸿洋 来源:博客园 发布于: 2015-05-14
  2254  次浏览      33
 

1、概述

无意中翻到的FoldingLayout的介绍的博客,以及github地址。感觉很nice呀,于是花了点时间研究以及编写,本篇博客将带大家从最基本的原理分析,一步一步的实现我们的FoldingLayout,当然了,如果你能力过硬,可以直接下载github上的代码进行学习。

博客基本分为以下几个部分:

1、Matrix的setPolyToPoly使用

2、在图片上使用渐变和阴影

3、初步的FoldingLayout的实现,完成图片的折叠显示(可控制折叠次数、包含阴影的绘制)

4、引入手势,手指可以可以FoldingLayout的折叠

5、结合DrawerLayout实现折叠式侧滑

6、结合SlidingPaneLayout实现折叠式侧滑

ok,贴下部分的效果图:

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

ok,对应上述4.

对应上述5。

ok,挑选了部分图,不然太占篇幅了。

那么接下来,我们就按照顺序往下学习了~~~

2、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的时候进行图像的绘制,效果为:

如果你已经在纸上稍微的画了dst的四个点,那么这个结果你一定不陌生。

可以看到我们通过matrix.setPolyToPoly实现了图片的倾斜,那么引入到折叠的情况,假设折叠两次,大家有思路么,考虑一下,没有的话,继续往下看。

3、引入阴影

其实阴影应该在实现初步的折叠以后来说,这样演示其实比较方便,但是为了降低其理解的简单性,我们先把阴影抽取出来说。

假设我们现在要给上图加上阴影,希望的效果图是这样的:

可以看到我们左侧加入了一点阴影,怎么实现呢?

主要还是利用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 实战 实现圆形、圆角图片

4、初步实现折叠

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

妹子折叠成了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了。

阴影这里大家可以换个明亮点的图片去看看~~

好了,由于篇幅原因,剩下的内容将在下一篇继续完成,下一篇将展示如何将简单的图片的折叠,转化为对一个布局内所有控件的折叠效果,以及引入手势、和DrawerLayout等结合应用到侧滑中去。对于类似这种效果的,一定要拿出稿纸笔去画一画,否则很难弄明白。

   
2254 次浏览       33
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
最新活动计划
嵌入式软件架构设计 12-11[北京]
LLM大模型与智能体开发实战 12-18[北京]
嵌入式软件测试 12-25[北京]
AI原生应用的微服务架构 1-9[北京]
AI大模型编写高质量代码 1-14[北京]
需求分析与管理 1-22[北京]

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   

Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合

领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...