Android自己定义控件——3D画廊和图像矩阵
转载请注明出处:http://blog.csdn.net/allen315410/article/details/39932689
1.3D画廊的实现
我们知道android系统已经为我们提供好了一个展示图片的“容器”——Gallery,可是这个Gallery显示的效果是平面化的,动态效果不强。
这里,我们动手做一个自己定义的Gallery组件。实现图片的3D效果展示。想想应该不错吧。先看看效果图:
实现这个3D效果的Gallery该怎么做呢?首先。分析一下,
1,展示图片。系统自带Gallery组件,能够基于这个Gallery组件扩展我们所须要的效果。
2。展示效果须要进行3D成像。
3,展示的图片下方须要显示图片的倒影。
4。展示图片的倒影须要加上“遮罩”效果。
好了,问题列好了,我们一个个来解决吧!
代码量不多,直接上代码好了。
package com.example.gallery.view; 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; @SuppressWarnings("deprecation")
public class CustomGallery extends Gallery { /** Gallery的中心点 */
private int galleryCenterPoint = 0;
/** 摄像机对象 */
private Camera camera; public CustomGallery(Context context, AttributeSet attrs) {
super(context, attrs);
// 启动getChildStaticTransformation
setStaticTransformationsEnabled(true);
camera = new Camera();
} /**
* 当Gallery的宽和高改变时回调此方法。第一次计算gallery的宽和高时,也会调用此方法
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO Auto-generated method stub
super.onSizeChanged(w, h, oldw, oldh); galleryCenterPoint = getGalleryCenterPoint(); } /**
* 返回gallery的item的子图形的变换效果
*
* @param t
* 指定当前item的变换效果
*/
@Override
protected boolean getChildStaticTransformation(View child, Transformation t) {
int viewCenterPoint = getViewCenterPoint(child); // view的中心点
int rotateAngle = 0; // 旋转角度,默觉得0 // 假设view的中心点不等于gallery中心,两边图片须要计算旋转的角度
if (viewCenterPoint != galleryCenterPoint) {
// gallery中心点 - view中心点 = 差值
int diff = galleryCenterPoint - viewCenterPoint;
// 差值 / 图片的宽度 = 比值
float scale = (float) diff / (float) child.getWidth();
// 比值 * 最大旋转角度 = 终于view的旋转角度(最大旋转角度定为50度)
rotateAngle = (int) (scale * 50); if (Math.abs(rotateAngle) > 50) {// 当终于旋转角度 》 最大旋转角度,要改成50或-50
rotateAngle = rotateAngle > 0 ? 50 : -50;
}
} // 设置变换效果前。须要把Transformation中的上一个item的变换效果清除
t.clear();
t.setTransformationType(Transformation.TYPE_MATRIX); // 设置变换效果的类型为矩阵类型
startTransformationItem((ImageView) child, rotateAngle, t);
return true;
} /**
* 设置变换的效果
*
* @param iv
* gallery的item
* @param rotateAngle
* 旋转的角度
* @param t
* 变换的对象
*/
private void startTransformationItem(ImageView iv, int rotateAngle,
Transformation t) {
camera.save(); // 保存状态
int absRotateAngle = Math.abs(rotateAngle); // 1.放大效果(中间的图片要比两边的图片大)
camera.translate(0, 0, 100f); // 给摄像机定位
int zoom = -250 + (absRotateAngle * 2);
camera.translate(0, 0, zoom); // 2.透明度(中间的图片全然显示,两边有一定的透明度)
int alpha = (int) (255 - (absRotateAngle * 2.5));
iv.setAlpha(alpha); // 3.旋转(中间的图片没有旋转角度。仅仅要不在中间的图片都有旋转角度)
camera.rotateY(rotateAngle); Matrix matrix = t.getMatrix(); // 变换的矩阵,将变换效果加入到矩阵中
camera.getMatrix(matrix); // 把matrix矩阵给camera对象。camera对象会把上面加入的效果转换成矩阵加入到matrix对象中
matrix.preTranslate(-iv.getWidth() / 2, -iv.getHeight() / 2); // 矩阵前乘
matrix.postTranslate(iv.getWidth() / 2, iv.getHeight() / 2); // 矩阵后乘 camera.restore(); // 恢复之前保存的状态
} /**
* 获取Gallery的中心点
*
* @return
*/
private int getGalleryCenterPoint() {
return this.getWidth() / 2;
} /**
* 获取item上view的中心点
*
* @param v
* @return
*/
private int getViewCenterPoint(View v) {
return v.getWidth() / 2 + v.getLeft(); // 图片宽度的一半+图片距离屏幕左边距
} }
代码中有凝视。大家能够看着凝视理解代码,我在这里要是说怎么考虑的,显得特别麻烦!这里另一个非常重要的概念——矩阵。这个我留在以下去解说。往下看吧。
获取图片的工具类:
package com.example.gallery.view; import java.lang.ref.SoftReference;
import java.util.Hashtable; import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader.TileMode;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.Log; public class ImageUtil { private static final String TAG = "ImageUtil";
/** 缓存集合 */
private static Hashtable<Integer, SoftReference<Bitmap>> mImageCache //
= new Hashtable<Integer, SoftReference<Bitmap>>(); /**
* 依据id返回一个处理后的图片
*
* @param res
* @param resID
* @return
*/
public static Bitmap getImageBitmap(Resources res, int resID) {
// 先去集合中取当前resID是否已经拿过图片,假设集合中有,说明已经拿过。直接使用集合中的图片返回
SoftReference<Bitmap> reference = mImageCache.get(resID);
if (reference != null) {
Bitmap bitmap = reference.get();
if (bitmap != null) {// 从内存中取
Log.i(TAG, "从内存中取");
return bitmap;
}
}
// 假设集合中没有。就调用getInvertImage得到一个图片,须要向集合中保留一张,最后返回当前图片
Log.i(TAG, "又一次载入");
Bitmap invertBitmap = getInvertBitmap(res, resID);
// 在集合中保存一份,便于下次获取时直接在集合中获取
mImageCache.put(resID, new SoftReference<Bitmap>(invertBitmap));
return invertBitmap;
} /**
* 依据图片的id,获取到处理之后的图片
*
* @param resID
* @return
*/
public static Bitmap getInvertBitmap(Resources res, int resID) {
// 1.获取原图
Bitmap sourceBitmap = BitmapFactory.decodeResource(res, resID); // 2.生成倒影图片
Matrix m = new Matrix(); // 图片矩阵
m.setScale(1.0f, -1.0f); // 让图片依照矩阵进行反转
Bitmap invertBitmap = Bitmap.createBitmap(sourceBitmap, 0,
sourceBitmap.getHeight() / 2, sourceBitmap.getWidth(),
sourceBitmap.getHeight() / 2, m, false); // 3.两张图片合成一张图片
Bitmap resultBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(),
(int) (sourceBitmap.getHeight() * 1.5 + 5), Config.ARGB_8888);
Canvas canvas = new Canvas(resultBitmap); // 为合成图片指定一个画板
canvas.drawBitmap(sourceBitmap, 0f, 0f, null); // 将原图片画在画布的上方
canvas.drawBitmap(invertBitmap, 0f, sourceBitmap.getHeight() + 5, null); // 将倒影图片画在画布的下方 // 4.加入遮罩效果
Paint paint = new Paint();
// 设置遮罩的颜色。这里使用的是线性梯度
LinearGradient shader = new LinearGradient(0,
sourceBitmap.getHeight() + 5, 0, resultBitmap.getHeight(),
0x70ffffff, 0x00ffffff, TileMode.CLAMP);
paint.setShader(shader);
// 设置模式为:遮罩,取交集
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
canvas.drawRect(0, sourceBitmap.getHeight() + 5,
sourceBitmap.getWidth(), resultBitmap.getHeight(), paint); return resultBitmap;
}
}
这个工具类就是获取整个图片的,包含实现图片的倒影和遮罩效果。看凝视。这里须要解说的是,假设避免OOM,这是一个较复杂的概念,不是一两句话就能讲清楚的。android下载入图片非常easy就处理OOM。当然了,避免OOM的方式有非常多,我在这是使用了内存缓存机制来避免了,即使用Java给我们提供好的“软引用”来解决。接下来,就是怎么引用这个画廊组件了。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black" > <com.example.gallery.view.CustomGallery
android:id="@+id/customgallery"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</com.example.gallery.view.CustomGallery> </RelativeLayout>
package com.example.gallery; import com.example.gallery.view.CustomGallery;
import com.example.gallery.view.ImageUtil; import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Gallery.LayoutParams;
import android.widget.ImageView; public class MainActivity extends Activity { /** 图片资源数组 */
private int[] imageResIDs; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageResIDs = new int[]{//
R.drawable.imgres_01, //
R.drawable.imgres_02, //
R.drawable.imgres_03, //
R.drawable.imgres_04, //
R.drawable.imgres_05, //
R.drawable.imgres_06, //
R.drawable.imgres_07, //
R.drawable.imgres_08, //
R.drawable.imgres_01, //
R.drawable.imgres_02, //
R.drawable.imgres_03, //
R.drawable.imgres_04, //
R.drawable.imgres_05, //
R.drawable.imgres_06, //
R.drawable.imgres_07, //
R.drawable.imgres_08 //
};
CustomGallery customGallery = (CustomGallery) findViewById(R.id.customgallery);
ImageAdapter adapter = new ImageAdapter();
customGallery.setAdapter(adapter);
} public class ImageAdapter extends BaseAdapter { @Override
public int getCount() {
// TODO Auto-generated method stub
return imageResIDs.length;
} @Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return imageResIDs[position];
} @Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ImageView imageView;
if (convertView != null) {
imageView = (ImageView) convertView;
} else {
imageView = new ImageView(MainActivity.this);
}
Bitmap bitmap = ImageUtil.getImageBitmap(getResources(),
imageResIDs[position]);
BitmapDrawable drawable = new BitmapDrawable(bitmap);
drawable.setAntiAlias(true); // 消除锯齿
imageView.setImageDrawable(drawable);
LayoutParams params = new LayoutParams(240, 320);
imageView.setLayoutParams(params);
return imageView;
}
}
}
===========================================华丽丽的切割线=============================================
2.Android的矩阵基础
UI开发过程中,我们常常须要对图片进行处理。常见的如贴图,复杂一些的还有位置变换、旋转、滤镜特效等。以下简介一下关于图片处理的一些基本知识和原理。
1 基本概念
对于图片的处理。最常使用到的数据结构是Bitmap,它包括了一张图片全部的数据,这些数据数据包括那些内容呢?简单说来就是由点阵和颜色值组成的,所谓点阵就是一个在概念上是Width * Height的矩阵。每个元素相应着图片的一个像素,也就是说,点阵保存着图片的空间位置信息;而颜色值即ARGB,分别相应透明度、红、绿、蓝这四个通道分量,每个通道用8比特定义,所以一个颜色值就是一个int整型。能够表示256*256*256种颜色值。
Android中我们经常使用到这么几个常量:ARGB_8888、ARGB_4444、RGB_565。
这几个常量事实上就是告诉系统怎样对图片的颜色值进行处理,比如ARGB_8888是告诉系统透明度、R、G、B在颜色值中分别用8bit表示,这时颜色值为32bit。这种定义可以表示最多的颜色值。图片质量也是最好的。ARGB_4444则是每一个通道用4bit表示。这样颜色值仅仅用16bit。节省了空间,可是却仅仅能表示16*16*16种颜色,也就是说图片非常失去非常多彩色信息;RGB_565类型的颜色值相同是16bit,可是它丢弃了透明度信息,可以表示32*64*32种颜色值。
2 颜色矩阵
颜色矩阵是一个5*4的矩阵,用来对图片颜色值进行处理。
定义颜色矩阵和颜色值例如以下例如以下:
进行例如以下矩阵运算:
结果R为4*1的矩阵,这个矩阵就是新的颜色值,R中每一个通道的值分别例如以下:
R’ = a*R + b*G + c*B + d*A + e;
G’ = f*R + g*G + h*B + i*A + j;
B’ = k*R + l*G + m*B + n*A + o;
A’ = p*R + q*G + r*B + s*A + t;
这样看起来也许非常抽象,非常难理解颜色矩阵和结果R直接的关系,我们如果颜色矩阵值例如以下所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWxsZW4zMTU0MTA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
那么结果为:
R’ = R;
G’ = G;
B’ = B;
A’ = A;
也就是说,新的颜色值跟原先的一样!再看一个样例。颜色矩阵取值为:
结果为:
R’ = R + 100;
G’ = G + 100;
B’ = B;
A’ = A;
新的颜色值中,红色通道值和绿色通道值分别添加了100,此时图片会泛黄(由于R + G = Yellow)。
从上面的几个样例我们非常easy就能明确颜色矩阵中的每一个分量(每一列)的意义:
第一行决定红色,
第二行决定绿色。
第三行决定蓝色。
第四行决定了透明度。
第五列是颜色的偏移量。
至此我们应该能理解怎样通过颜色矩阵来改变颜色值的各个分量了。
以下是用于Android的一段代码。用于将图片处理成泛黄的效果:
public static Bitmap testBitmap(Bitmap bitmap){
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Config.RGB_565); Canvas canvas = new Canvas(output); Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
float[] array = {1,0,0,0,100,
0,1,0,0,100,
0,0,1,0,0,
0,0,0,1,0};
cm.set(array);
paint.setColorFilter(new ColorMatrixColorFilter(cm)); canvas.drawBitmap(bitmap, 0, 0, paint);
return output;
}
3 坐标变换矩阵
对图片的操作除了颜色值的处理外,最经常使用的就是空间坐标的变换了。常见的效果有平移、旋转、拉伸等,这事实上也是通过一个矩阵来完毕的。
坐标变换矩阵是一个3*3的矩阵,通过与一个类似(X,Y,1)的坐标值的矩阵乘法运算。可以将这个坐标值转换成一个新的坐标值,计算步骤例如以下:
结果为:
x’=a*x+b*y+c
y’=d*x+e*y+f
同颜色矩阵一样,假设坐标变换矩阵例如以下,则新的坐标值X、Y添加50,也就是说图片的每一点都平移了(50,50)的距离,即图片总体平移到了(50,50)坐标处。
假设坐标变换矩阵例如以下,则全部的X、Y坐标都增大两倍,也就是说图片被放大了两倍,其它缩放效果原理类似。
更复杂一点的还有旋转效果,一个旋转变换矩阵例如以下:
结果为x’ = xcosθ – ysinθ 与 y’ = xsinθ + ycosθ,这个结果的效果是绕原点逆时针旋转θ度角。
以下是用于Android的一段演示样例代码,用于将图片平移。也就是裁剪的效果。其它效果能够參照相应坐标变换矩阵改动就可以:
public static Bitmap test1Bitmap(Bitmap bitmap){
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Config.RGB_565); Canvas canvas = new Canvas(output); Paint paint = new Paint();
Matrix cm = new Matrix(); float[] array = {1,0,50,
0,1,50,
0,0,1};
cm.setValues(array);
canvas.drawBitmap(bitmap, cm, paint);
return output;
}
以下将介绍几种经常使用的变换矩阵:
1.旋转
绕原点逆时针旋转θ度角的变换公式是 x' = xcosθ − ysinθ
与 y' = xsinθ + ycosθ
2. 缩放
变换后长宽分别放大x'=scale*x;y'=scale*y.
3.切变
4.反射
5.正投影
Android的图像矩阵绝对不止这些,这是一个非常复杂的知识。涉及到大学相关数学的课程,能了解大学线性代数里的矩阵知识,对学习Android下的图像矩阵有非常好的帮助,在这里限于篇幅。我仅仅做了简单的基础解说,基本能够理解,能够使用就可以,假设想深入学习一下的话,请查看下方的资料链接,去下载我今天上传到CSDN资源库里面的资料。
Android自己定义控件——3D画廊和图像矩阵的更多相关文章
- Android自己定义控件系列五:自己定义绚丽水波纹效果
尊重原创!转载请注明出处:http://blog.csdn.net/cyp331203/article/details/41114551 今天我们来利用Android自己定义控件实现一个比較有趣的效果 ...
- Android自己定义控件:进度条的四种实现方式
前三种实现方式代码出自: http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/ (源代码下载)http://down ...
- android 自己定义控件
Android自己定义View实现非常easy 继承View,重写构造函数.onDraw.(onMeasure)等函数. 假设自己定义的View须要有自己定义的属性.须要在values下建立attrs ...
- Android自己定义控件皮肤
Android自己定义控件皮肤 对于Android的自带控件,其外观仅仅能说中规中矩,而我们平时所示Android应用中,一个简单的button都做得十分美观.甚至于很多button在按下时的外观都有 ...
- android 自己定义控件属性(TypedArray以及attrs解释)
近期在捣鼓android 自己定义控件属性,学到了TypedArray以及attrs.在这当中看了一篇大神博客Android 深入理解Android中的自己定义属性.我就更加深入学习力一番.我就沿着这 ...
- Android自己定义控件之应用程序首页轮播图
如今基本上大多数的Android应用程序的首页都有轮播图.就是像下图这种(此图为转载的一篇博文中的图.拿来直接用了): 像这种组件我相信大多数的应用程序都会使用到,本文就是自己定义一个这种组件,能够动 ...
- Android自己定义控件(状态提示图表)
[工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处.尊重分享成果] 1 背景 前面分析那么多系统源代码了.也该暂停下来歇息一下,趁昨晚闲着看见一个有意思的需求就操 ...
- Android自己定义控件系列二:自己定义开关button(一)
这一次我们将会实现一个完整纯粹的自己定义控件,而不是像之前的组合控件一样.拿系统的控件来实现.计划分为三部分:自己定义控件的基本部分,自己定义控件的触摸事件的处理和自己定义控件的自己定义属性: 以下就 ...
- Android 自己定义控件开发入门(二)
上一次我们讲了一堆实现自己定义控件的理论基础.列举了View类一些能够重写的方法,我们对这些方法的重写是我们继承View类来派生自己定义控件的关键 我通过一个最简单的样例给大家展示了这一个过程,不管是 ...
随机推荐
- Robot Framework: 自定义自己的python库
利用Robot Framework编写测试用例,往往需要开发自己的关键字,有的关键字需要通过自己编写python代码来实现.这在rf中,就需要自己定义python库.这个过程其实不复杂,本文来介绍下. ...
- 《Python简明教程》总结
Python经典教程<Python简明教程> 目录: 为什么Python 安装Python 体验Python Python数据类型 运算符与表达式 控制流 函数 模块 数据结构 解决问题 ...
- 基于visual Studio2013解决C语言竞赛题之0903文件读写
题目
- android intent收集转载汇总
Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS); ComponentName comp = ...
- log翻硬币
若果有一组硬币,(假定有十个),每一个硬币仅仅有两个面,正面用以表示.反面用零表示. 给定目标(初始状态)1111100000 正正正正正反反反反反 (目标状态) 1000011101 正反反反反 ...
- C# - Byte类型与String类型互转
byte[] bs = Encoding.UTF8.GetBytes("你的字符串"); string str = Encoding.UTF8.GetString(bs);
- TTimerThread和TThreadedTimer(都是通过WaitForSingleObject和CreateEvent来实现的)
//////////////////////////////////////////////////// // // // ThreadedTimer 1.24 // // // // Copyrig ...
- 动态Pivot(1)
原文 http://book.51cto.com/art/200710/58874.htm 7.7 动态Pivot 作为另外一个练习,假设你要编写一个存储过程,它生成动态Pivot查询.这个存储过程 ...
- 用Eclipse做Android开发时出现java.lang.NoClassDefFoundError问题
之前有遇到过这个问题,后来解决了,今天又遇到了,但是忘了当时是怎么解决的,费了好长时间,终于又找回解决的方法,现在记录下来,以防以后又遇到. 这个错误出现在我的某一个Activity,但是我反复确认了 ...
- perl use base 代替 @ISA
packge Mule; use base ("Horse", "donkey"); # 声明一个超类 它是下面东西的缩写: package Mule; BEG ...