今天来实现一个3D画廊的效果,这个效果的难点在于计算旋转角度,当然里面会有好几个知识点要讲,针对Paint的2个方法,一个是setShader(),一个是setXfermode(),首先看下大概的效果,

大概是这种,这是我在网上随便找了一个类似的图片,因为我的效果还没写,没关系,这没啥影响,这个效果我准备分开写,然后后面合成起来,上面的效果可以分为如下几步

1:首先是怎么截取一张图片中的一部分

2:怎么把多张图片合成一张图片

3:怎么生成倒影效果,

4:怎么改变倒影中的图片透明度

5:最后一步是怎么计算它滑动时候的旋转角度问题

首先把第一步的效果实现出来,就是怎么截图一张图片中的一部分,如图:

现在开始写代码

public class MainActivity extends Activity {private ImageView iv,originalIv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv = (ImageView) findViewById(R.id.iv);
        originalIv = (ImageView) findViewById(R.id.originalIv);
        originalIv.setImageResource(R.mipmap.gird);
        Bitmap bitmap = compoundBitmap(R.mipmap.gird);
        iv.setImageBitmap(bitmap);
    }/**
     * 截图图片
     * @param resId 图片 id
     */
    public Bitmap compoundBitmap(int resId){Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(),resId);//把资源图片变成一个Bitmap对象
        //生成下面的一半图片
        Matrix matrix = new Matrix();
        Bitmap invertBitmap = Bitmap.createBitmap(originalBitmap,0,originalBitmap.getHeight()/2,originalBitmap.getWidth(),originalBitmap.getHeight()/2,matrix,false);
        return  invertBitmap;
    }
}

效果:第一个而是原图 第二张是截图一半的图,为了进行更好的对比,

上面就使用了一个Bitmap的createBitmap方法就可以实现截图了,现在对createBitmap的方法参数进行说明

public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)

参数说明:

source:原位图

x:位图的x轴坐标

y:位图的y轴坐标

width:新位图的宽度

height:新位图的高度

m:矩阵

filter:这个参数比较难懂 先看下google官网文档介绍

 true if the source should be filtered.
*                   Only applies if the matrix contains more than just
*                   translation

当进行的不只是平移变换时,filter参数为true可以进行滤波处理,有助于改善新图像质量;flase时,计算机不做过滤处理

画图解释下:


现在怎么考虑把这二张图片合成一张图片了,这个要用到Canvas了,之前我们canvas是从自定义view中的onDraw()方法给我们提供的,现在我们要创建一个画布,然后在这画布上把这二张图片画上去,

代码如下:

/**
 * 合成图片
 * @param resId 图片 id
 */
public Bitmap compoundBitmap(int resId){Paint paint = new Paint();
    Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(),resId);//把资源图片变成一个Bitmap对象
    //生成下面的一半图片
    Matrix matrix = new Matrix();
    Bitmap invertBitmap = Bitmap.createBitmap(originalBitmap,0,originalBitmap.getHeight()/2,originalBitmap.getWidth(),originalBitmap.getHeight()/2,matrix,false);
    //创建一个空的位图
    Bitmap compoundBitmap =  Bitmap.createBitmap(originalBitmap.getWidth(),originalBitmap.getHeight()+invertBitmap.getHeight(), Bitmap.Config.ARGB_8888);

    Canvas canvas = new Canvas(compoundBitmap);
    canvas.drawBitmap(originalBitmap,0,0,paint);
    canvas.drawBitmap(invertBitmap,0,originalBitmap.getHeight(),paint);

    return  compoundBitmap;
}

效果:


ok,现在我们实现了2张图片怎么合成一张图片,首先是先绘制一个空的位图,这个位图的宽和高要设置好,然后通过canvas的drawBitmap()把这二张图片绘制到画布上去,绘制上要注意坐标点就行,还有个问题就是截图的一半图片不是垂直的拼接在下面,上面我们讲截图图片一部分的方法createBitmap()方法进行参数说明的时候,其中有一个Matirs矩阵,当时为什么要使用这个方法呢?Bitmap的createBitmap有很多重载的方法,就是因为提供了Matris类,让我们可以对位图进行像动画那样操作,现在看下Matirs一些常用的方法:


我们发现Matris提供了平移,缩放,旋转,透明等操作,我们只要加入一行代码就可以让下面的一半图片改成在原来的图片上进行垂直后显示,

matrix.setScale(1,-1);

public void setScale(float sx, float sy)

参数说明:

sx:x轴水平翻转 -1表示翻转 1表示不翻转

sy:y轴方向翻转  -1表示翻转 1表示不翻转

/**
 * 合成图片
 * @param resId 图片 id
 */
public Bitmap compoundBitmap(int resId){Paint paint = new Paint();
    Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(),resId);//把资源图片变成一个Bitmap对象
    //生成下面的一半图片
    Matrix matrix = new Matrix();
    matrix.setScale(1,-1);
    Bitmap invertBitmap = Bitmap.createBitmap(originalBitmap,0,originalBitmap.getHeight()/2,originalBitmap.getWidth(),originalBitmap.getHeight()/2,matrix,false);
    //创建一个空的位图
    Bitmap compoundBitmap =  Bitmap.createBitmap(originalBitmap.getWidth(),originalBitmap.getHeight()+invertBitmap.getHeight()+10, Bitmap.Config.ARGB_8888);//+10是为了2张图片之间有空隙

    Canvas canvas = new Canvas(compoundBitmap);
    canvas.drawBitmap(originalBitmap,0,0,paint);
    canvas.drawBitmap(invertBitmap,0,originalBitmap.getHeight()+10,paint);
    return  compoundBitmap;
}

效果:


现在我们把前三步实现了,还有个倒影图片的透明度问题,这就使用到Paint中的setShader()方法了,这是设置画笔的颜色渲染方法,发现Shader有5个子类,每一个子类实现的功能效果不一样,


Shader的直接子类以及作用:
BitmapShader    : 位图图像渲染
LinearGradient  : 线性渲染
RadialGradient  : 环形渲染
SweepGradient   : 扫描渐变渲染/梯度渲染
ComposeShader   : 组合渲染,可以和其他几个子类组合起来使用

现在举例讲每个大概的功能,见识下平时效果使我们想不到是通过这个api实现的,

BitmapShader 

先看下它的构造函数:

public BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY)

参数说明:

bitmap:渲染器使用的位图

tileX:在位图x轴方向渲染器的平铺模式

tileY:在位图y轴方向渲染器的平铺模式

渲染器的模式有如下三种:

public enum TileMode {/**
     * replicate the edge color if the shader draws outside of its
     * original bounds
     */
    CLAMP   (0),
    /**
     * repeat the shader's image horizontally and vertically
     */
    REPEAT  (1),
    /**
     * repeat the shader's image horizontally and vertically, alternating
     * mirror images so that adjacent images always seam
     */
    MIRROR  (2);

    TileMode(int nativeInt) {this.nativeInt = nativeInt;
    }final int nativeInt;
}

CLAMP:超过边缘部分会在超过边缘范围给染色  也就是拉伸  比如imageview大小为100,100,但是bitmap大小为80,80如果使用了这个模式,会把其余的20都给染色

REPEAT:横向和纵向的重复渲染器图片,平铺

MIRROR:横向不断翻转重复,纵向不断翻转重复

我们平时使用比较多的圆角图片也可以使用这个实现

public class CustomImageView extends View {private Paint mPaint;
    private ShapeDrawable mShapeDrawable;
    private Bitmap shaderBitmap;
    private int bitmapWidth;
    private int bitmapHeight;
    private BitmapShader mBitmapShader;
    public CustomImageView(Context context) {this(context, null);
    }public CustomImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);
    }public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        mPaint = new Paint();
    }@Override
    protected void onDraw(Canvas canvas) {super.onDraw(canvas);
        mShapeDrawable = new ShapeDrawable(new OvalShape());
        mShapeDrawable.getPaint().setShader(mBitmapShader);
        //这是设置一个矩形区域  表示这个drawable所绘制的区域
        mShapeDrawable.setBounds(20, 20, bitmapWidth, bitmapHeight);
        mShapeDrawable.draw(canvas);

    }public void setImageRes(int resId){shaderBitmap = BitmapFactory.decodeResource(getContext().getResources(),resId);
        bitmapWidth = shaderBitmap.getWidth();
        bitmapHeight = shaderBitmap.getHeight();
        mBitmapShader = new BitmapShader(shaderBitmap, Shader.TileMode.MIRROR,Shader.TileMode.REPEAT);
    }
}

效果:

其实还有别的图形模式,比如圆角矩形

public ShapeDrawable(Shape s) {this(new ShapeState(null), null);

    mShapeState.mShape = s;
}

多个Shape子类

这样好像看起来不容易理解,现在再举个例子,我先准备一张图片,

public class CustomView extends View {private Paint mPaint;
    public CustomView(Context context) {this(context,null);
    }public CustomView(Context context, AttributeSet attrs) {this(context, attrs,0);
    }public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        mPaint = new Paint();
    }@Override
    protected void onDraw(Canvas canvas) {super.onDraw(canvas);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
        // 设置shader
        BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        mPaint.setShader(shader);
        // 用设置好的画笔绘制一个矩形
        mPaint.setColor(Color.GREEN);
        canvas.drawRect(0, 0, 1000, 1000, mPaint);
        mPaint.reset();
    }
}

我x轴和y轴都是重复这张图片的,效果如下:

我现在把x轴改为MIRROR也就是镜像,y轴不变,

这就是x轴镜像y轴重复这个图片的效果,现在就剩下一个模式没讲就是CLAMP,就是边缘像素拉伸,

为了演示这个效果,要换一张图片,

我现在把x轴改为REPEAT,y轴改为CLAMP,效果如下:


如果x,y轴都是clamp模式也就是边缘拉伸,如下

我现在再把图片边缘颜色改下,这下总可以知道这个模式是干嘛的吧,'

x,y轴方向都CLAMP模式,效果如下:

这个黑色没显示出来,是因为我没画好,好了,这个模式我相信我已经讲的很清楚了,为了弄清这个花了很久的时间去想怎么写出来才看的懂这个模式,

LinearGradient

这个是线性渐变颜色渲染器

它有2个构造函数

public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,TileMode tile)

参数说明:

x0: 渐变起始点x坐标

y0:渐变起始点y坐标

x1:渐变结束点x坐标

y1:渐变结束点y坐标

color0: 起始渐变色

color1: 结束渐变色

tile: 渲染器平铺模式

这就是2个点起始点为(x0,y0)终点为(x1,y1) 起始点的颜色为color0,终点的颜色为color1

现在写个例子再理解上面几个坐标

public class CustomImageView extends View {private Paint mPaint;
    private LinearGradient linearGradient,linearGradient1,linearGradient2;
    public CustomImageView(Context context) {this(context, null);
    }public CustomImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);
    }public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        linearGradient = new LinearGradient(0, 0, 100, 100,Color.RED , Color.YELLOW,
                Shader.TileMode.REPEAT);
        linearGradient1 = new LinearGradient(0, 0, 0, 100,Color.RED , Color.YELLOW,
                Shader.TileMode.REPEAT);

        linearGradient2= new LinearGradient(0, 0, 100, 0,Color.RED , Color.YELLOW,
                Shader.TileMode.REPEAT);
    }@Override
    protected void onDraw(Canvas canvas) {super.onDraw(canvas);
        //设置渲染器
        mPaint.setShader(linearGradient);
        canvas.drawColor(Color.BLUE);
        canvas.drawRect(0, 0, 300, 300, mPaint);
        mPaint.setShader(linearGradient1);
        canvas.drawRect(0, 350, 300, 650, mPaint);
        mPaint.setShader(linearGradient2);
        canvas.drawRect(0, 700, 300, 1000, mPaint);
    }
}

效果:

看下第二个和第三个LinearGradient构造函数中给的值

linearGradient1 = new LinearGradient(0, 0, 0, 100,Color.RED , Color.YELLOW, Shader.TileMode.REPEAT);
linearGradient2= new LinearGradient(0, 0, 100, 0,Color.RED , Color.YELLOW, Shader.TileMode.REPEAT);

分析图:

分析图二:

关于它的最好一个参数模式在讲BitmapShader已经讲的很清楚了,不在这多讲,

RadialGradient 环形

首先看下它的构造函数,

public RadialGradient(float centerX, float centerY, float radius,int centerColor, int edgeColor, TileMode tileMode)

参数说明:

centerX:圆心的x轴坐标

centerY:圆心的y轴坐标

radius:圆的半径

centerColor:圆心的颜色

edgeColor:圆心边缘的颜色

tileMode:平铺模式

public class CustomView extends View {private Paint mPaint;
    private RadialGradient mRadialGradient;
    public CustomView(Context context) {this(context,null);
    }public CustomView(Context context, AttributeSet attrs) {this(context, attrs,0);
    }public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        //1.圆心X坐标2.Y坐标3.半径 4.颜色数组 5.相对位置数组,可为null 6.渲染器平铺模式
        mRadialGradient = new RadialGradient(240, 360, 60, Color.RED, Color.YELLOW,
                Shader.TileMode.REPEAT);
    }@Override
    protected void onDraw(Canvas canvas) {super.onDraw(canvas);
        mPaint.setColor(Color.GRAY);
        mPaint.setShader(mRadialGradient);
        mPaint.setAntiAlias(true);
        canvas.drawCircle(240, 360, 200, mPaint);
    }
}

效果图:

通过这个可以做一个垃圾版的水波纹效果,

代码如下:

public class CustomView extends View {private Paint mPaint;
    private Handler mHandler;
    private int radius = 20;
    private int tag = 100001;
    private RadialGradient mRadialGradient;
    public CustomView(Context context) {this(context,null);
    }public CustomView(Context context, AttributeSet attrs) {this(context, attrs,0);
    }public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        mHandler = new Handler(){@Override
            public void handleMessage(Message msg) {super.handleMessage(msg);
                if(msg.what==tag){if(radius>200){radius=20;
                    }else{radius+=5;
                    }invalidate();
                }}};
    }@Override
    protected void onDraw(Canvas canvas) {super.onDraw(canvas);
        mPaint.setColor(Color.GRAY);
        mRadialGradient = new RadialGradient(240, 360, radius, Color.RED, Color.YELLOW, Shader.TileMode.REPEAT);
        mPaint.setShader(mRadialGradient);
        canvas.drawCircle(240, 360, 200, mPaint);
        mHandler.sendEmptyMessageAtTime(tag,300);
    }
}

RadialGradient还有一个构造函数:

public RadialGradient(float centerX, float centerY, float radius,int colors[], float stops[], TileMode tileMode)

构造函数

centerX:圆心的x轴坐标

cneterY:圆心的y轴坐标

radius:圆的半径

colors:int[] 数组其实就是多个颜色值的集合

stops:该数组中每一个stop对应colors数组中每个颜色在半径中的相对位置,stop取值范围为[0,1],0表示圆心位置,1表示圆周位置。如果stops数组为null,那么Android会自动为colors设置等间距的位置

tileMode:模式

好了 这个有时间再把剩余2个补上,不然这个博客还不知道什么时候写完,现在讲paint另一个很重要的方法

mPaint.setXfermode(Xfermode);

Xfermode就是图形混合模式,它有三个子类

关于这个知识点一时也讲不清楚,准备另外写一篇博客讲解下,在这里贴下代码,自己可以去百度下,

/**
 * 合成图片
 * @param resId 图片 id
 */
public Bitmap compoundBitmap(int resId){Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(),resId);//把资源图片变成一个Bitmap对象
    //生成下面的一半图片
    Matrix matrix = new Matrix();
    matrix.setScale(1,-1);
    Bitmap invertBitmap = Bitmap.createBitmap(originalBitmap,0,originalBitmap.getHeight()/2,originalBitmap.getWidth(),originalBitmap.getHeight()/2,matrix,false);
    //创建一个空的位图
    Bitmap compoundBitmap =  Bitmap.createBitmap(originalBitmap.getWidth(),originalBitmap.getHeight()+invertBitmap.getHeight()+10, Bitmap.Config.ARGB_8888);//+10是为了2张图片之间有空隙

    Canvas canvas = new Canvas(compoundBitmap);
    canvas.drawBitmap(originalBitmap,0,0,null);
    canvas.drawBitmap(invertBitmap,0,originalBitmap.getHeight()+10,null);
    Paint paint = new Paint();
    // 设置渐变颜色
    LinearGradient shader = new LinearGradient(0, originalBitmap.getHeight() + 10, 0, compoundBitmap.getHeight(), 0x70ffffff, 0x00ffffff, Shader.TileMode.CLAMP);
    paint.setShader(shader);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    canvas.drawRect(0, originalBitmap.getHeight() + 5, originalBitmap.getWidth(), compoundBitmap.getHeight(), paint);
    return  compoundBitmap;
}

渐变颜色染色器上面是讲了,现在看下效果,

ok,现在完成了倒影的效果,

public class MainActivity extends Activity {private Gallery gallery;
    private MyAdapter adapter;
    private int[] ids ={R.mipmap.a,R.mipmap.b,R.mipmap.c,R.mipmap.d,R.mipmap.e,R.mipmap.f,R.mipmap.g,R.mipmap.h,R.mipmap.i};
    private int screenWidth;
    private int screenHeigh;
    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getScreenWidthAndHeight();
        gallery = (Gallery) findViewById(R.id.gallery);
        adapter = new MyAdapter();
        gallery.setAdapter(adapter);
    }class MyAdapter extends BaseAdapter {@Override
        public int getCount() {return ids.length;
        }@Override
        public Object getItem(int position) {return null;
        }@Override
        public long getItemId(int position) {// TODO Auto-generated method stub
            return 0;
        }@Override
        public View getView(int position, View convertView, ViewGroup parent) {ImageView iv = null;
            if(convertView == null) {iv = new ImageView(MainActivity.this);
            } else {iv = (ImageView) convertView;
            }Bitmap bitmap = Utils.compoundBitmap(MainActivity.this.getResources(), ids[position]);
            BitmapDrawable bd = new BitmapDrawable(bitmap);
            bd.setAntiAlias(true);    // 消除锯齿
            iv.setImageDrawable(bd);
            Gallery.LayoutParams params = new Gallery.LayoutParams(screenWidth/2, screenHeigh/2);
            iv.setLayoutParams(params);
            return iv;
        }}/**
     * 获取屏幕的宽和高
     */
    public void getScreenWidthAndHeight(){DisplayMetrics dm = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(dm);
            screenWidth = dm.widthPixels;
            screenHeigh = dm.heightPixels;
    }
}

效果:

现在就是计算滑动时候旋转的角度问题了,看下原型图

发现这有几个效果

1:中间的图片明显比二边的图片要大,
2:中间的图片没有旋转,二边的图片还有旋转

3:在滑动过程中还有二边的图片还有透明的效果

尼玛  鬼做的出来,经过百度再百度,找到一些别人实现好的方法,懂了原理,原来是这样的,首先给别人点赞,太牛逼

这个控件我们是自定义Gallery来实现的,

public class MyGallery extends Gallery {public MyGallery(Context context) {super(context);
    }public MyGallery(Context context, AttributeSet attrs) {super(context, attrs);
    }public MyGallery(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
    }@Override
    protected boolean getChildStaticTransformation(View child, Transformation t) {return super.getChildStaticTransformation(child, t);
    }
}

发现什么也没写,对吧 ,是的 在思考,

上面自定义的gallery中重写了protected boolean getChildStaticTransformation(View child, Transformation t)方法,这是表示gallery在滑动过程中返回它item的子视图的效果,

参数说明:

child:就是你gallery中滑动的veiw

t:就是当前item的变化效果,

现在我在adapter中的getView()方法中给每个imageview设置下tag,tag的值就是当前的position,当我们运行起来什么都没干的时候,getChildStaticTransformation()方法打印出来的log

05-26 03:23:16.931 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:16.931 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:16.941 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->1
05-26 03:23:16.968 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:16.968 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:16.968 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:16.968 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->1
05-26 03:23:16.968 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:25.644 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->0
05-26 03:23:25.644 32525-32525/com.example.customgallery E/MyGalleryHAHA: tag--->1
05-26 03:23:59.180 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:23:59.180 755-755/? E/MyGalleryHAHA: tag--->

05-26 03:23:59.189 755-755/? E/MyGalleryHAHA: tag--->1
05-26 03:23:59.204 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:23:59.204 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:23:59.204 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:23:59.204 755-755/? E/MyGalleryHAHA: tag--->1
05-26 03:23:59.204 755-755/? E/MyGalleryHAHA: tag--->0

进来就发现只显示2个item,当我往右滑动的一个item的时候,也就是说第二个item出现的时候,log

05-26 03:26:25.186 755-755/? E/MyGalleryHAHA: tag--->2
05-26 03:26:25.187 755-755/? E/MyGalleryHAHA: tag--->2
05-26 03:26:25.678 755-755/? E/MyGalleryHAHA: tag--->1
05-26 03:26:25.678 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:26:25.682 755-755/? E/MyGalleryHAHA: tag--->0
05-26 03:26:25.682 755-755/? E/MyGalleryHAHA: tag--->1

发现是刚滑动出来的先打印出来,其他没发现有什么规律,

旋转的计算分析图:

gallery中的代码全部如下:

package com.example.customgallery;

import android.content.Context;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Transformation;
import android.widget.Gallery;
import android.widget.ImageView;

/**
 * Created by admin on 2016/5/26.
 */
public class MyGallery extends Gallery {private int centerPoint;
    private static final String TAG = "MyGalleryHAHA";
    private Camera mCamera;
    private int  maxRoate = 60;//旋转的最大角度
    public MyGallery(Context context) {super(context);
        mCamera = new Camera();
        setStaticTransformationsEnabled(true);
    }public MyGallery(Context context, AttributeSet attrs) {super(context, attrs);
        mCamera = new Camera();
        setStaticTransformationsEnabled(true);
    }public MyGallery(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        mCamera = new Camera();
        setStaticTransformationsEnabled(true);
    }@Override
    protected boolean getChildStaticTransformation(View child, Transformation t) {int viewCenterPoint = getItemViewCenterPoint(child);   // item的中心点
        int rotateAngle = 0;      // 默认旋转角度为0
        // 如果当前的View的中心点不等于gallery的中心点, 就是两边的图片, 需要计算旋转角度
        if(viewCenterPoint != centerPoint) {// gallery中心点 - 图片中心点 = 差值
            int diff = centerPoint - viewCenterPoint;
            // 差值 / 图片的宽度 = 比值
            float scale = (float)diff / (float)child.getWidth();
            // 比值 * 最大旋转角度 = 最终的旋转角度
            rotateAngle = (int) (scale * maxRoate);
            if(Math.abs(rotateAngle) > maxRoate) {    // 当前角度超过了50, 需要赋值到50 或者 -50
                rotateAngle = rotateAngle > 0 ? maxRoate : -maxRoate;
            }}// 设置变换效果之前, 需要把Transformation中的上一个item的变换效果清楚
        t.clear();
        t.setTransformationType(Transformation.TYPE_MATRIX);   // 设置变换效果的类型为矩阵类型
        setItemStartAnim((ImageView) child, rotateAngle, t);
        return true;
    }/**
     * 设置变换效果
     * @param iv galleryitem
     * @param rotateAngle 旋转的角度
     * @param t 变换的对象
     */
    private void setItemStartAnim(ImageView iv, int rotateAngle, Transformation t) {mCamera.save();       // 保存状态

        int absRotateAngle = Math.abs(rotateAngle);    // 取旋转角度的绝对值

        // 放大效果
        mCamera.translate(0, 0, 100f);    // 给摄像机定位

        int zoom = -240 +(absRotateAngle * 2);
        mCamera.translate(0, 0, zoom);

        // 透明度(中间的图片是完全显示, 两边有一定的透明度)
        int alpha = (int) (255 - (absRotateAngle * 2.5));
        iv.setAlpha(alpha);       // 透明度取值范围: 0 ~ 255, 0 就是完全隐藏, 255 完全显示

        // 旋转(在中间的图片没有旋转角度, 只要不在中间就有旋转角度)
        mCamera.rotateY(rotateAngle);

        Matrix matrix = t.getMatrix();     // 变换的矩阵, 需要把变换的效果添加到矩阵中

        // 给matrix赋值
        mCamera.getMatrix(matrix);    // 把matrix矩阵给camera对象, camera对象就会把上面添加的效果转换成矩阵添加到matrix对象中

        // 矩阵前乘
        matrix.preTranslate(-iv.getWidth() / 2, -iv.getHeight() / 2);

        // 矩阵后乘
        matrix.postTranslate(iv.getWidth() / 2, iv.getHeight() / 2);

        mCamera.restore(); // 恢复到之前保存的状态
    }/**
     * 获取gallery的中心点
     * @return
     */
    public int getCenterPoint(){return getWidth()/2;
    }/**
     * 获取item  view的中心点
     */
    public int getItemViewCenterPoint(View itemView) {if (itemView != null) {return itemView.getWidth() / 2 + itemView.getLeft();
        }return  0;
    }@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);
        centerPoint = getCenterPoint();
    }
}

在这要注意点:

getChildStaticTransformation()方法的返回值一定要返回true,否则没效果,

最终的效果如下:

写了2天多了,终于写完了!也许写的不够好,但真的尽力了!

android 自定义控件实现3D画廊效果相关推荐

  1. [原创]自定义ViewPager实现3D画廊效果

    经常在群里看到有些开发者在提问:怎么实现3D画廊效果,没思路. 有人出谋划策,你重写onTouch,在里面去判断:或者你去重写滑动监听事件,滑动的时候去动态设置左右两边的图片的大小和缩放效果.可能你们 ...

  2. Android自定义控件之3D上下翻页效果的倒计时控件

    这是一个自定义的倒计时控件,具有3D上下翻页翻转效果.最近项目中需要做一个倒计时控件,需要和iOS端的效果保持一样.大致效果是这样的,如下图所示: 由于暂时还不会怎么样制作gif动态图,所以想看具体效 ...

  3. android viewPage 打造3d画廊

    之前学习时学习了ViewPager打造3d画廊,今天找出来把他贴上去,给需要的小伙伴用!废话不多说直接贴! 一.Activity /*** 3da 画廊*/ public class Main3Act ...

  4. 使用RecyclerView实现旋转3D画廊效果

    3D旋转画廊效果实现有哪些方式? 1.Gallery实现(官方已不推荐使用). 2.RecyclerView通过自定义LayoutManager实现. 一.简介 RecyclerView是google ...

  5. Android自带组件之Gallery 实现3D画廊效果

    1: 首先我们要了解到这个该控件的常用属性: 如图: 2:通过该组件定义属于我们自己的组件 iphone 中的coverflow中图片切换是有旋转和缩放效果的,而自带的gallery中并没有实现.因此 ...

  6. android画廊效果的轮播图,轮播图(3d画廊效果)

    首先需要将轮播图的依赖导入 implementation 'com.github.xiaohaibin:XBanner:1.6.1' 接下来就是在项目目录下bulidgradle中导入(allproj ...

  7. Android 简单的3D动画效果

    在Android Api11之后,加入了属性动画,参加我的另一篇博文:Android Property Animation 属性动画 也加入了一些View的属性和对应的操作方法: iv.setTran ...

  8. android+ebook控件,Android 自定义控件 eBook 翻书效果

    Book.java文件: package com.book; import Android.app.Activity; import android.os.Bundle; import android ...

  9. android viewpager画廊,Android使用ViewPager实现画廊效果

    按照国际惯例,先上效果图 其实这跟普通的ViewPager原理都一样,需要改变的地方就是: 1.增加滑进和滑出的动画效果 2.缩小ViewPager的大小,给屏幕上留出上一张和下一张视图的空间 布局文 ...

  10. Android自定义控件eBook翻书效果

    转载请注明转载地址: http://wallage.blog.163.com/blog/static/1738962420108915833584/ 效果图: Book.java文件: package ...

最新文章

  1. zsh 每次打开Terminal都需要source bash_profile问题
  2. java定时器返回future_java 定时器线程池(ScheduledThreadPoolExecutor)的实现
  3. 021_Jedis的ZSet数据类型
  4. [转载]为什么使用 SLF4J 而不是Log4J来做Java 日志
  5. cc、gcc、g++ 的区别和联系
  6. 记-安装pillow
  7. 【MATLAB统计分析与应用100例】案例007:matlab数据的极差归一化变换
  8. Apollo技能图谱2.0焕新发布 更新7大能力91个知识点
  9. Windows下使用VS2008+CUDA3.0开发的详细配置 (Setup CUDA 3.0 on VS2008 in Windows)
  10. Linux 网络服务之FTP 文件传输
  11. [转载] 在python中、关于全局变量和局部变量、以下_python中的全局变量和局部变量
  12. 西瓜书读书笔记4-Sigmoid函数
  13. 同余方程(Day 2)
  14. 《电子商务安全》考试重点/学习重点
  15. 【开发工具】Blender制作简单动画
  16. Linux查看内存的方法
  17. MSM7225 600MHZ CPU和高通MSM 7201A 528MHz CPU的比较
  18. 后端程序员生产力工具合集
  19. Flutter 画笔绘制二维码扫描框
  20. python爬取高匿代理IP(再也不用担心会进小黑屋了)

热门文章

  1. 通过Adobe Acrobat DC和iText.jar完成通过pdf模板生成pdf
  2. Mixly Aduino 超声波~蜂鸣器
  3. 【日常】解决问题:SSR1080端口被占用的问题
  4. 射频回波损耗、反射系数、电压驻波比、S参数的含义与关系
  5. Druid连接池原理
  6. 《计算机操作系统》| 第一章 操作系统引论 知识梳理
  7. PackageManager的基本使用
  8. 怎么添加网络扫描仪到计算机,MAC如何添加网络扫描仪
  9. 删除团队项目集合(TFS2010)
  10. java定时器表达式_Java Quartz表达式每分钟执行1次