Android 自定义控件之---3D画廊

一、效果图:

这里写图片描述

二、首先实现基本的框架

1、创建一个类继承Gallery

public class GalleryView extends Gallery {

    public GalleryView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GalleryView(Context context) {
        this(context, null);
    }

    public GalleryView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

}

2、创建布局,在布局里引用这个类

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.android.imooc.gallery.GalleryView
        android:id="@+id/galleryView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </com.android.imooc.gallery.GalleryView>

</LinearLayout>

3、在主页里添加

public class GalleryActivity extends Activity {
    private GalleryView mGallery;
    private int mResIds[] = {
        R.drawable.pic_1,   
        R.drawable.pic_2,   
        R.drawable.pic_3,   
        R.drawable.pic_4,   
        R.drawable.pic_5,   
        R.drawable.pic_6,   
        R.drawable.pic_7,   
        R.drawable.pic_8
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gallery);

        mGallery = (GalleryView) findViewById(R.id.galleryView);
        mGallery.setAdapter(new GalleryAdapter());
    }

    private class GalleryAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return mResIds.length;
        }

        @Override
        public Object getItem(int position) {
            return mResIds[position];
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView iv = null;
            if (convertView == null) {
                iv = new ImageView(GalleryActivity.this);
            }else {
                iv = (ImageView) convertView;
            }

            iv.setImageResource(mResIds[position]);
            LayoutParams params = new LayoutParams(160, 260);
            iv.setLayoutParams(params);
            iv.setScaleType(ScaleType.CENTER_CROP);
            return iv;
        }

    }
}

三、图片处理;

1、如果是有倒影的图片,就不能直接iv.setImageResource(mResIds[position]);
而是通过工具类实现了倒影后把图片设置到imageview里

Bitmap reverseBitmap = ImageUtil.getReverseBitmapById(mResIds[position]);
iv.setImageBitmap(reverseBitmap);

2、开始写方法getReverseBitmapById(mResIds[position])
分析:
首先获取原图
然后根据原图创建一张根据y坐标对称的倒立图
把两张图片合成一张图片
把下面的图片加上遮罩

1)创建原图

Bitmap sourceBitmap = BitmapFactory.decodeResource(context.getResources(), resId);

2)绘制原图一半的图片
以下面图片分析,这张图片的x轴是0,y轴是getHeight,
中间点的坐标是(1,getHeight()/2);
这里写图片描述

//绘制原图的下一半图片
Matrix matrix = new Matrix();
//倒影
matrix.setScale(1, -1);
Bitmap inverseBitmap = Bitmap.createBitmap(sourceBitmap, 0, sourceBitmap.getHeight() / 2, sourceBitmap.getWidth(), sourceBitmap.getHeight()/2, matrix, false);

3)创建合成图片

 //合成图片
Bitmap groupbBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), sourceBitmap.getHeight() + sourceBitmap.getHeight() / 2 + padding,
                sourceBitmap.getConfig());

4)根据合成图片创建画板

Canvas canvas = new Canvas(groupbBitmap);
//把原图画在合成图片的上面
 canvas.drawBitmap(sourceBitmap, 0, 0, null);
 //以图片的左上角与坐标
 canvas.drawBitmap(inverseBitmap, 0, sourceBitmap.getHeight() + padding, null);

5)添加遮罩,主要使用了线性渲染器

//添加遮罩
Paint paint = new Paint();
//TileMode.CLAMP表示渲染时一直往下延伸
TileMode tile = TileMode.CLAMP;
LinearGradient shader = new LinearGradient(0, sourceBitmap.getHeight() + padding, 0, groupbBitmap.getHeight(), Color.TRANSPARENT,  Color.BLACK, tile);
paint.setShader(shader);

//这里取的是矩形与图片的交集,所以用的是DST_IN
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));

gCanvas.drawRect(0, sourceBitmap.getHeight() + padding, sourceBitmap.getWidth(), groupbBitmap.getHeight(), paint);

遮罩模式图
这里写图片描述

6)全部代码

/**
 * 根据图片id获得有倒影的图片
 * @param resId
 * @return
 */
public static Bitmap getReverseBitmapById(Context context, int resId) {
    int padding  = context.getResources().getDimensionPixelOffset(R.dimen.image_paddding);//图片的间距
    //绘制原图
    Bitmap sourceBitmap = BitmapFactory.decodeResource(context.getResources(), resId);

    //图片的默认矩阵
//      float[] values = {
//              1.0f, 0f, 0f,
//              0f, 1.0f, 0f,
//              0f, 0f, 1.0f
//      };


    //绘制原图的下一半图片
    Matrix matrix = new Matrix();
    matrix.setScale(1, -1);
    //matrix.setValues(values);
    Bitmap inverseBitmap = Bitmap.createBitmap(sourceBitmap, 0, sourceBitmap.getHeight() / 2, sourceBitmap.getWidth(), sourceBitmap.getHeight()/2, matrix, false);


       //合成图片
       Bitmap groupbBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), sourceBitmap.getHeight() + sourceBitmap.getHeight() / 2 + padding,
            sourceBitmap.getConfig());


       Canvas gCanvas = new Canvas(groupbBitmap);
       //把原图画在合成图片的上面
       gCanvas.drawBitmap(sourceBitmap, 0, 0, null);
       //以图片的左上角与坐标
       gCanvas.drawBitmap(inverseBitmap, 0, sourceBitmap.getHeight() + padding, null);

    //添加遮罩
    Paint paint = new Paint();
    TileMode tile = TileMode.CLAMP;
    LinearGradient shader = new LinearGradient(0, sourceBitmap.getHeight() + padding, 0, groupbBitmap.getHeight(), Color.TRANSPARENT,  Color.BLACK, tile);
    paint.setShader(shader);
    //这里取的是矩形与图片的交集,所以用的是DST_IN
    paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
    gCanvas.drawRect(0, sourceBitmap.getHeight() + padding, sourceBitmap.getWidth(), groupbBitmap.getHeight(), paint);

    return groupbBitmap;
}

四、旋转处理

这里写图片描述
从上面的图片分析:
必须实现三种效果
1)旋转: 中间的图片完全在中间时没有旋转角度,只要移动就有
2)透明度:中间的图片完全显示,旁边的图片有些透明
3)放大效果:中间的图片大,旁边的图片越来越小
那如何实现呢?记得在gallery里有个方法getChildStaticTransformation,就是用来实现子view的变化效果的

好了,开始

1、判断图片是否在中间,只要判断child与gallery的中心点是否一致即可

 child.getLeft() + child.width/2 = gallery.widht/2;

2、如果图片不在中间,设
两个中心的距离为dis = 20dp
图片的最大旋转角度 maxDegree = 50°
那图片的旋转角度 = dis / child.width * maxDegree
这里写图片描述

3、分别获得gallery的中心点与图片的中心点

/**
     * gallery中心点
     * @return
     */
    public int getGalleryCenterX(){
        return this.getWidth() / 2;
    }


    /**
     * child中心点
     * @return
     */
    public int getChildCenterX(View child){
        return child.getLeft() + child.getWidth() / 2;
    }

4、 得到旋转的角度,设置参数

/**
     * 实现子view的变化效果 Transformation指定当前item的效果
     */
    @Override
    protected boolean getChildStaticTransformation(View child, Transformation t) {
        int rotateAngle = 0;
        // 如果child的中心点与gallery的中心点不一致,需要计算旋转角度
        int childCenterX = getChildCenterX(child);
        if (childCenterX != mGalleryCenterX) {
            // 两个中心点距离
            int distance = mGalleryCenterX - childCenterX;
            float percent = distance * 1.0f / child.getWidth();
            rotateAngle = (int) (percent * mMaxAngle);// 得到旋转的角度

            // 因为distance有可能大于图片的宽度,所以得到角度有可能大于最大的角度
            if (Math.abs(rotateAngle) > mMaxAngle) {
                rotateAngle = rotateAngle > 0 ? mMaxAngle : -mMaxAngle;
            }
        }
        //设置变化之前,要把上面的一个动画清除
        t.clear();
        //设置变化的效果为矩阵类型
        t.setTransformationType(Transformation.TYPE_MATRIX);
        //开始旋转
        startAnimate(child, rotateAngle, t);

        return true;
    }

5、处理动画效果
gallery实现的动画效果必须使用android.graphics.Camera类,
在构造方法里直接
mCamera = new Camera();

mCamera使用时必须先mCamera.save(),结束后mCamera.restore();

/**
     * 开始动画效果
     * @param child
     * @param rotateAngle
     * @param t
     */
    private void startAnimate(View child, int rotateAngle, Transformation t) {
            ImageView iv = (ImageView) child;
            int absAngle = Math.abs(rotateAngle);

            mCamera.save();
            //3.实现放大效果

            //仔细看图片,发现图片在x,y轴上都没有变化,但有一边缩小,另一边放大,是如何实现的?
            //这里就要用到了z轴了,只要改变轴的数值就能实现了
            mCamera.translate(0, 0, 100);
            int zoom = -250 + (absAngle * 2);
            mCamera.translate(0, 0, zoom);

            //2.设置透明度 (0到255) 255完全显示,中间的absAngle=0,所以没有透明度
            iv.setAlpha(255- absAngle * 2.5f);

            //3.旋转
            mCamera.rotateY(rotateAngle);

            //4.转换成矩阵
            Matrix matrix = t.getMatrix();
            //给matrix赋值
            mCamera.getMatrix(matrix);
            //矩阵前乘
            matrix.preTranslate(-iv.getWidth()/2, -iv.getHeight()/2);
            //矩阵后乘
            matrix.postTranslate(iv.getWidth()/2, iv.getHeight()/2);
    }

矩阵后前示意图 (矩阵后乘与其相反移动)
这里写图片描述

6、现在基本功能都实现了,是不是发现有锯齿不好看,如何去除锯齿呢?
有一个包装类可以实现BitmapDrawable

所以在GalleryAdapter里的适配器的getView方法里就必须改代码
原来是

Bitmap bm = ImageUtil.getReverseBitmapById(GalleryActivity.this, mResIds[position]);
iv.setImageBitmap(bm);

现在加包装

Bitmap bm = ImageUtil.getReverseBitmapById(GalleryActivity.this, mResIds[position]);

//去除锯齿
BitmapDrawable bd = new BitmapDrawable(bm);
bd.setAntiAlias(true);
iv.setImageDrawable(bd);

7、为了提高图片的速度,我使用了lruCache来存取图片,如何实现?
首先在适配器定义

LruCache<String, Bitmap> mCache ;
String key = "key";

在构造方法里初始化mCache

public GalleryAdapter(){
            if (mCache == null) {
                // 最大使用的内存空间
                int maxSize = (int) (Runtime.getRuntime().freeMemory() / 4);
                mCache = new LruCache<String, Bitmap>(maxSize) {
                    @Override
                    protected int sizeOf(String key, Bitmap value) {
                        return value.getRowBytes() * value.getHeight();
                    }
                };
            }
        }

最后在getview方法里

Bitmap bm = mCache.get(key);
      if (bm == null) {
        bm =  ImageUtil.getReverseBitmapById(GalleryActivity.this, mResIds[position]);
      }else {
    mCache.put(key, bm);
}

五、贴上全部代码

1、主页

/**
 * @描述 TODO
 * @项目名称 App_imooc
 * @包名 com.android.imooc.gallery
 * @类名 GalleryActivity
 * @author chenlin
 * @date 2012年6月5日 下午9:16:33
 * @version 1.0
 */

@SuppressWarnings("all")
public class GalleryActivity extends Activity {
    private GalleryView mGallery;
    private int mResIds[] = {
        R.drawable.pic_1,   
        R.drawable.pic_2,   
        R.drawable.pic_3,   
        R.drawable.pic_4,   
        R.drawable.pic_5,   
        R.drawable.pic_6,   
        R.drawable.pic_7,   
        R.drawable.pic_8
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gallery);

        mGallery = (GalleryView) findViewById(R.id.galleryView);
        mGallery.setAdapter(new GalleryAdapter());
    }

    private class GalleryAdapter extends BaseAdapter {
        LruCache<String, Bitmap> mCache ;
        String key = "key";

        public GalleryAdapter(){
            if (mCache == null) {
                // 最大使用的内存空间
                int maxSize = (int) (Runtime.getRuntime().freeMemory() / 4);
                mCache = new LruCache<String, Bitmap>(maxSize) {
                    @Override
                    protected int sizeOf(String key, Bitmap value) {
                        return value.getRowBytes() * value.getHeight();
                    }
                };
            }
        }

        @Override
        public int getCount() {
            return mResIds.length;
        }

        @Override
        public Object getItem(int position) {
            return mResIds[position];
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView iv = null;
            if (convertView == null) {
                iv = new ImageView(GalleryActivity.this);
            }else {
                iv = (ImageView) convertView;
            }

            Bitmap bm = mCache.get(key);
            if (bm == null) {
                bm =  ImageUtil.getReverseBitmapById(GalleryActivity.this, mResIds[position]);
            }else {
                mCache.put(key, bm);
            }

            //去除锯齿
            BitmapDrawable bd = new BitmapDrawable(bm);
            bd.setAntiAlias(true);
            iv.setImageDrawable(bd);

            LayoutParams params = new LayoutParams(160, 260);
            iv.setLayoutParams(params);
            iv.setPadding(0, 0, 10, 0);
            iv.setScaleType(ScaleType.FIT_XY);
            return iv;
        }

    }
}

2、自定义gallery视图

/**
 * @描述 TODO
 * @项目名称 App_imooc
 * @包名 com.android.imooc.async
 * @类名 GalleryView
 * @author chenlin
 * @date 2012年6月5日 下午9:14:48
 * @version 1.0
 */

@SuppressWarnings("all")
public class GalleryView extends Gallery {

    private static final String TAG = "gallery";
    private int mGalleryCenterX = 0;
    private int mMaxAngle = 50;// 最大旋转角度
    private Camera mCamera;

    public GalleryView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GalleryView(Context context) {
        this(context, null);
    }

    public GalleryView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setStaticTransformationsEnabled(true);
        mCamera = new Camera();
    }

    /**
     * 实现子view的变化效果 Transformation指定当前item的效果
     */
    @Override
    protected boolean getChildStaticTransformation(View child, Transformation t) {
        int rotateAngle = 0;
        // 如果child的中心点与gallery的中心点不一致,需要计算旋转角度
        int childCenterX = getChildCenterX(child);
        if (childCenterX != mGalleryCenterX) {
            // 两个中心点距离
            int distance = mGalleryCenterX - childCenterX;
            float percent = distance * 1.0f / child.getWidth();
            rotateAngle = (int) (percent * mMaxAngle);// 得到旋转的角度

            // 因为distance有可能大于图片的宽度,所以得到角度有可能大于最大的角度
            if (Math.abs(rotateAngle) > mMaxAngle) {
                rotateAngle = rotateAngle > 0 ? mMaxAngle : -mMaxAngle;
            }
        }
        //设置变化之前,要把上面的一个动画清除
        t.clear();
        //设置变化的效果为矩阵类型
        t.setTransformationType(Transformation.TYPE_MATRIX);
        //开始旋转
        startAnimate(child, rotateAngle, t);

        return true;
    }

    /**
     * 开始动画效果
     * @param child
     * @param rotateAngle
     * @param t
     */
    private void startAnimate(View child, int rotateAngle, Transformation t) {
        //if (child instanceof ImageView) {
            ImageView iv = (ImageView) child;
            int absAngle = Math.abs(rotateAngle);

            mCamera.save();
            //3.实现放大效果

            //仔细看图片,发现图片在x,y轴上都没有变化,但有一边缩小,另一边放大,是如何实现的?
            //这里就要用到了z轴了,只要改变轴的数值就能实现了
            mCamera.translate(0, 0, 100);
            int zoom = -250 + (absAngle * 2);
            mCamera.translate(0, 0, zoom);

            //2.设置透明度 (0到255) 255完全显示,中间的absAngle=0,所以没有透明度
            iv.setAlpha((int) (255 - absAngle * 2.5));

            //3.旋转
            mCamera.rotateY(rotateAngle);

            //4.转换成矩阵
            Matrix matrix = t.getMatrix();
            //给matrix赋值
            mCamera.getMatrix(matrix);

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

            mCamera.restore();
        //}

    }

    /**
     * gallery中心点
     * 
     * @return
     */
    public int getGalleryCenterX() {
        return this.getWidth() / 2;
    }

    /**
     * child中心点
     * 
     * @return
     */
    public int getChildCenterX(View child) {
        return child.getLeft() + child.getWidth() / 2;
    }

    /**
     * 设置最大旋转角度
     * 
     * @param maxAngel
     */
    public void setAngle(int maxAngel) {
        this.mMaxAngle = maxAngel;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mGalleryCenterX = getGalleryCenterX();
        Logger.i(TAG, "mGalleryCenterX = " + mGalleryCenterX);
        Logger.i(TAG, "w/2 = " + w / 2);
        super.onSizeChanged(w, h, oldw, oldh);
    }
}

3、工具类

/**
 * @描述         图片处理工具
 * @项目名称      App_imooc
 * @包名         com.android.imooc.gallery
 * @类名         ImageUtil
 * @author      chenlin
 * @date        2012年9月5日 下午10:05:38
 * @version     1.0
 */

public class ImageUtil {

    /**
     * 根据图片id获得有倒影的图片
     * @param resId
     * @return
     */
    public static Bitmap getReverseBitmapById(Context context, int resId) {
        int padding  = context.getResources().getDimensionPixelOffset(R.dimen.image_paddding);//图片的间距
        //绘制原图
        Bitmap sourceBitmap = BitmapFactory.decodeResource(context.getResources(), resId);

        //图片的默认矩阵
//      float[] values = {
//              1.0f, 0f, 0f,
//              0f, 1.0f, 0f,
//              0f, 0f, 1.0f
//      };


        //绘制原图的下一半图片
        Matrix matrix = new Matrix();
        matrix.setScale(1, -1);
        //matrix.setValues(values);
        Bitmap inverseBitmap = Bitmap.createBitmap(sourceBitmap, 0, sourceBitmap.getHeight() / 2, sourceBitmap.getWidth(), sourceBitmap.getHeight()/2, matrix, false);


        //合成图片
        Bitmap groupbBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), sourceBitmap.getHeight() + sourceBitmap.getHeight() / 2 + padding,
                sourceBitmap.getConfig());


        Canvas gCanvas = new Canvas(groupbBitmap);
        //把原图画在合成图片的上面
        gCanvas.drawBitmap(sourceBitmap, 0, 0, null);
        //以图片的左上角与坐标
        gCanvas.drawBitmap(inverseBitmap, 0, sourceBitmap.getHeight() + padding, null);

        //添加遮罩
        Paint paint = new Paint();
        TileMode tile = TileMode.CLAMP;
        LinearGradient shader = new LinearGradient(0, sourceBitmap.getHeight() + padding, 0, groupbBitmap.getHeight(), 0x70ffffff,  Color.TRANSPARENT, tile);
        paint.setShader(shader);

        paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));

        gCanvas.drawRect(0, sourceBitmap.getHeight() + padding, sourceBitmap.getWidth(), groupbBitmap.getHeight(), paint);

        return groupbBitmap;
    }

}

六、源码下载

链接:http://pan.baidu.com/s/1boAkuy7 密码:kdx8

———————————————————————
(java 架构师全套教程,共760G, 让你从零到架构师,每月轻松拿3万)
有需求者请进站查看,非诚勿扰

https://item.taobao.com/item.htm?spm=686.1000925.0.0.4a155084hc8wek&id=555888526201

01.高级架构师四十二个阶段高
02.Java高级系统培训架构课程148课时
03.Java高级互联网架构师课程
04.Java互联网架构Netty、Nio、Mina等-视频教程
05.Java高级架构设计2016整理-视频教程
06.架构师基础、高级片
07.Java架构师必修linux运维系列课程
08.Java高级系统培训架构课程116课时
(送:hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门)
——————————————————————–

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lovoo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值