Android 自定义控件之---3D画廊
发布日期:2021-06-30 22:37:40 浏览次数:2 分类:技术文章

本文共 17491 字,大约阅读时间需要 58 分钟。

一、效果图:

这里写图片描述

二、首先实现基本的框架

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、创建布局,在布局里引用这个类

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_INpaint.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
mCache ;String key = "key";

在构造方法里初始化mCache

public GalleryAdapter(){            if (mCache == null) {                // 最大使用的内存空间                int maxSize = (int) (Runtime.getRuntime().freeMemory() / 4);                mCache = new LruCache
(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
mCache ; String key = "key"; public GalleryAdapter(){ if (mCache == null) { // 最大使用的内存空间 int maxSize = (int) (Runtime.getRuntime().freeMemory() / 4); mCache = new LruCache
(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; }}

六、源码下载

链接: 密码: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入门)
——————————————————————–

转载地址:https://lovoo.blog.csdn.net/article/details/51591580 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android Gallery Adapter的基本写法
下一篇:Java 数据结构之队列

发表评论

最新留言

不错!
[***.144.177.141]2024年04月29日 15时35分24秒