公司最近有一个需求,是打算做一个轮播图的展示界面,不过和传统意义上不同,并非是在手机app的顶部展示几张定时切换的固定大小宽高的图片,而是中间长方形,两边向里倾斜,形成对称感的特殊界面,如下图:
需要实现功能:无限循环,自动跳转,倒影效果。
(原本的企划是动画轮播的时候,下面会呈现一条Listview,里面会因为展示的不同界面而呈现不同的内容,但是后面放弃了。)
下面开始上代码:
MainActivity:
package com.example.gallery;
import com.example.gallery.view.MyGallery;
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.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Gallery.LayoutParams;
import android.widget.ImageView;
import android.widget.Toast;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends Activity {
/**
* 图片资源数组
*/
private int[] imageResIDs;
private MyGallery gallery;
private int index = 0;
private final int AUTOPLAY = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageResIDs = new int[]{R.drawable.a00, R.drawable.a01, R.drawable.a02, R.drawable.a03,
R.drawable.a04, R.drawable.a05,};
gallery = (MyGallery) findViewById(R.id.mygallery);
ImageAdapter adapter = new ImageAdapter();
gallery.setAdapter(adapter);
gallery.setSpacing(50); //图片之间的间距
gallery.setSelection((Integer.MAX_VALUE / 2) - (Integer.MAX_VALUE / 2) % imageResIDs.length);
gallery.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
// 设置点击事件监听
gallery.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(MainActivity.this, "当前位置position:"+position+"的图片被选中了", Toast.LENGTH_SHORT).show();
}
});
Timer timer = new Timer();
timer.schedule(task, 3000, 3000);
}
/**
* 定时器,实现自动播放
*/
private TimerTask task = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = AUTOPLAY;
index = gallery.getSelectedItemPosition();
index++;
handler.sendMessage(message);
}
};
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case AUTOPLAY:
gallery.setSelection(index);
break;
default:
break;
}
}
};
public class ImageAdapter extends BaseAdapter {
@Override
public int getCount() {
return Integer.MAX_VALUE;//用于循环滚动
}
@Override
public Object getItem(int position) {
if (position >= imageResIDs.length) {
position = position % imageResIDs.length;
}
return position;
}
@Override
public long getItemId(int position) {
if (position >= imageResIDs.length) {
position = position % imageResIDs.length;
}
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView != null) {
imageView = (ImageView) convertView;
} else {
imageView = new ImageView(MainActivity.this);
}
if (position >= imageResIDs.length) {
position = position % imageResIDs.length;
}
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;
}
}
}
自定义Gallery:
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;
public class MyGallery extends Gallery {
/** Gallery的中心点 */
private int galleryCenterPoint = 0;
/** 摄像机对象 */
private Camera camera;
public MyGallery(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) {
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;
}
}
布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black">
<com.example.gallery.view.MyGallery
android:id="@+id/mygallery"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</com.example.gallery.view.MyGallery>
</RelativeLayout>
~Demo下载链接~