现在的位置: 首页 > 综合 > 正文

Android 滑动效果入门篇(一)—— ViewFlipper Android 滑动效果入门篇(一)—— ViewFlipperAndroid 滑动效果入门篇(二)—— GalleryAndroid 滑动效果基础篇(三)—— Gallery仿图像集浏览Android 滑动效果基础篇(四)—— Gallery + GridViewAndroid 滑动效果进阶篇(五)—— 3D旋转Android 滑动效果进阶篇(六)—— 倒影效果

2017年12月12日 ⁄ 综合 ⁄ 共 42983字 ⁄ 字号 评论关闭

Android 滑动效果入门篇(一)—— ViewFlipper

分类: Android 13698人阅读 评论(19) 收藏 举报

ViewFilpper 是Android官方提供的一个View容器类,继承于ViewAnimator类,用于实现页面切换,也可以设定时间间隔,让它自动播放。
又ViewAnimator继承至于FrameLayout的,所以ViewFilpper的Layout里面可以放置多个View,继承关系如下:



本示例通过ViewFlipper和GestureDetector.OnGestureListener实现自动播放和手势滑屏事件,先看效果:



Activity

  1. import android.app.Activity;  
  2. import android.os.Bundle;  
  3. import android.view.GestureDetector;  
  4. import android.view.MotionEvent;  
  5. import android.view.ViewGroup.LayoutParams;  
  6. import android.view.animation.Animation;  
  7. import android.view.animation.AnimationUtils;  
  8. import android.widget.ImageView;  
  9. import android.widget.ViewFlipper;  
  10.   
  11. public class ViewFlipperActivity extends Activity implements android.view.GestureDetector.OnGestureListener {  
  12.     private int[] imgs = { R.drawable.img1, R.drawable.img2,  
  13.                       R.drawable.img3, R.drawable.img4, R.drawable.img5 };  
  14.   
  15.     private GestureDetector gestureDetector = null;  
  16.     private ViewFlipper viewFlipper = null;  
  17.   
  18.     private Activity mActivity = null;  
  19.   
  20.     @Override  
  21.     public void onCreate(Bundle savedInstanceState) {  
  22.         super.onCreate(savedInstanceState);  
  23.         setContentView(R.layout.main);  
  24.   
  25.         mActivity = this;  
  26.   
  27.         viewFlipper = (ViewFlipper) findViewById(R.id.viewflipper);  
  28.         gestureDetector = new GestureDetector(this);    // 声明检测手势事件  
  29.   
  30.         for (int i = 0; i < imgs.length; i++) {          // 添加图片源  
  31.             ImageView iv = new ImageView(this);  
  32.             iv.setImageResource(imgs[i]);  
  33.             iv.setScaleType(ImageView.ScaleType.FIT_XY);  
  34.             viewFlipper.addView(iv, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));  
  35.         }  
  36.           
  37.         viewFlipper.setAutoStart(true);         // 设置自动播放功能(点击事件,前自动播放)  
  38.         viewFlipper.setFlipInterval(3000);  
  39.         if(viewFlipper.isAutoStart() && !viewFlipper.isFlipping()){  
  40.             viewFlipper.startFlipping();  
  41.         }  
  42.     }  
  43.   
  44.     @Override  
  45.     public boolean onTouchEvent(MotionEvent event) {  
  46.         viewFlipper.stopFlipping();             // 点击事件后,停止自动播放  
  47.         viewFlipper.setAutoStart(false);      
  48.         return gestureDetector.onTouchEvent(event);         // 注册手势事件  
  49.     }  
  50.   
  51.     @Override  
  52.     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {  
  53.         if (e2.getX() - e1.getX() > 120) {            // 从左向右滑动(左进右出)  
  54.             Animation rInAnim = AnimationUtils.loadAnimation(mActivity, R.anim.push_right_in);  // 向右滑动左侧进入的渐变效果(alpha  0.1 -> 1.0)  
  55.             Animation rOutAnim = AnimationUtils.loadAnimation(mActivity, R.anim.push_right_out); // 向右滑动右侧滑出的渐变效果(alpha 1.0  -> 0.1)  
  56.   
  57.             viewFlipper.setInAnimation(rInAnim);  
  58.             viewFlipper.setOutAnimation(rOutAnim);  
  59.             viewFlipper.showPrevious();  
  60.             return true;  
  61.         } else if (e2.getX() - e1.getX() < -120) {        // 从右向左滑动(右进左出)  
  62.             Animation lInAnim = AnimationUtils.loadAnimation(mActivity, R.anim.push_left_in);       // 向左滑动左侧进入的渐变效果(alpha 0.1  -> 1.0)  
  63.             Animation lOutAnim = AnimationUtils.loadAnimation(mActivity, R.anim.push_left_out);     // 向左滑动右侧滑出的渐变效果(alpha 1.0  -> 0.1)  
  64.   
  65.             viewFlipper.setInAnimation(lInAnim);  
  66.             viewFlipper.setOutAnimation(lOutAnim);  
  67.             viewFlipper.showNext();  
  68.             return true;  
  69.         }  
  70.         return true;  
  71.     }  
  72.   
  73.     @Override  
  74.     public boolean onDown(MotionEvent e) {  
  75.         return false;  
  76.     }  
  77.   
  78.     @Override  
  79.     public void onLongPress(MotionEvent e) {  
  80.     }  
  81.   
  82.     @Override  
  83.     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {  
  84.         return false;  
  85.     }  
  86.   
  87.     @Override  
  88.     public void onShowPress(MotionEvent e) {  
  89.     }  
  90.   
  91.     @Override  
  92.     public boolean onSingleTapUp(MotionEvent e) {  
  93.         return false;  
  94.     }  
  95. }  

main.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <ViewFlipper  
  8.         android:id="@+id/viewflipper"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="fill_parent"/>  
  11.   
  12. </LinearLayout>  

示例分析

一、自动播放

ViewFlipper控件,是ImageView的容器,用于添加显示的图片资源,主要功能有两个:添加显示View和自动播放View

通过实现父类android.view.ViewGroup的addView(View child, ViewGroup.LayoutParams params)添加View资源,即图片和填充样式

启动自动播放View,可以通过设置如下三个成员函数实现:

1、 setAutoStart(true),设置是否自动播放功能,true为自动播放,false为不自动播放,开启自动播放设为true

2、 setFlipInterval(int milliseconds),设置View播放的时间间隔,如3000(3秒)

3、 startFlipping(),开始自动播放

停止自动播放View,设置成员函数如下:

1、 stopFlipping(),停止自动播放

2、 setAutoStart(false),停止自动播放,设为false


二、手势滑屏

手势滑动屏幕动画,是通过android.view.GestureDetector类检测各种手势事件实现的,该类有两个回调接口(Interface)

A、GestureDetector.OnDoubleTapListener,用来通知DoubleTap双击事件,类似于鼠标的双击事件,接口三个抽象回调函数如下

1、onDoubleTap(MotionEvent e):DoubleTap双击手势事件后通知(触发)

2、onDoubleTapEvent(MotionEvent e):DoubleTap双击手势事件之间通知(触发),包含down、up和move事件(这里指的是在双击之间发生的事件,例如在同一个地方双击会产生DoubleTap手势,而在DoubleTap手势里面还会发生down和up事件,这两个事件由该函数通知)

3、onSingleTapConfirmed(MotionEvent e):用来判定该次点击是SingleTap而不是DoubleTap,如果连续点击两次就是DoubleTap手势;那么如果只点击一次,系统等待一段时间后没有收到第二次点击则判定该次点击为SingleTap而不是DoubleTap,此时触发的就是SingleTapConfirmed事件

B、GestureDetector.OnGestureListener,用来通知普通的手势事件(down、longPress、scroll、up等),接口具体的六个抽象回调函数如下

1、onDown(MotionEvent e):down事件,表示按下事件

2、onSingleTapUp(MotionEvent e):一次点击up事件,表示按下后的抬起事件

3、onShowPress(MotionEvent e):down事件发生而move或则up还没发生前触发该事件,此事件一般用于通知用户press按击事件已发生

4、onLongPress(MotionEvent e):长按事件,down事件后up事件前的一段时间间隔后(由系统分配,也可自定义),如果仍然按住屏幕则视为长按事件

5、onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY):滑动手势事件,例如scroll事件后突然up,fling的速度大小由e每秒x和y改变大小决定

6、onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY):在屏幕上拖动事件,即down按下点——scroll拖动——up抬起点的move移动事件

本示例的滑动屏幕动画,仅用到了上面的GestureDetector.OnGestureListener及其onFling事件,具体实现步骤如下:

1、Activity实现android.view.GestureDetector.OnGestureListener 监听接口,并声明gestureDetector = new GestureDetector(this); 用于监听手势事件

2、在Activity的成员函数onTouchEvent(MotionEvent event)中,注册GestureDetector.OnGestureListener手势监听的gestureDetector.onTouchEvent(event);事件接口

3、在GestureDetector.OnGestureListener回调函数onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) 中,实现滑屏动画

三、屏幕渐变效果

1、 当手势从左向右滑动时,图片是左进右出

if (e2.getX() - e1.getX() > 120) ,即up终点(e2)与down起点(e1)的滑动距离大于120,来检测从左向右滑动事件

push_left_in.xml —— 左进渐变效果

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <set xmlns:android="http://schemas.android.com/apk/res/android" >  
  3.     <translate  
  4.         android:duration="1500"  
  5.         android:fromXDelta="100%p"  
  6.         android:toXDelta="0" />  
  7.   
  8.     <alpha  
  9.         android:duration="1500"  
  10.         android:fromAlpha="0.1"  
  11.         android:toAlpha="1.0" />  
  12. </set>  

push_left_out.xml —— 右出渐变效果

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <set xmlns:android="http://schemas.android.com/apk/res/android" >  
  3.     <translate  
  4.         android:duration="1500"  
  5.         android:fromXDelta="0"  
  6.         android:toXDelta="-100%p" />  
  7.   
  8.     <alpha  
  9.         android:duration="1500"  
  10.         android:fromAlpha="1.0"  
  11.         android:toAlpha="0.1" />  
  12. </set>  

2、 当手势从右向左滑动时,图片是右进左出

if (e2.getX() - e1.getX() < -120) ,即up终点(e2)与down起点(e1)的滑动距离小于-120,来检测从右向左滑动事件

push_right_in.xml —— 右进渐变效果

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <set xmlns:android="http://schemas.android.com/apk/res/android" >  
  3.     <translate  
  4.         android:duration="1500"  
  5.         android:fromXDelta="-100%p"  
  6.         android:toXDelta="0" />  
  7.   
  8.     <alpha  
  9.         android:duration="1500"  
  10.         android:fromAlpha="0.1"  
  11.         android:toAlpha="1.0" />  
  12. </set>  

push_right_out.xml —— 右出渐变效果

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <set xmlns:android="http://schemas.android.com/apk/res/android" >  
  3.     <translate  
  4.         android:duration="1500"  
  5.         android:fromXDelta="0"  
  6.         android:toXDelta="100%p" />  
  7.   
  8.     <alpha  
  9.         android:duration="1500"  
  10.         android:fromAlpha="1.0"  
  11.         android:toAlpha="0.1" />  
  12. </set>  

其中,android:duration表示渐变持续时间;translate表示位移变换;alpha表示透明度变换

translate

android:fromXDelta="-100%p"   android:toXDelta="0" 表示图片从左进入,从不可见到可见

android:fromXDelta="0" android:toXDelta="100%p"    表示图片从右滑出,从可见到不可见

alpha

android:fromAlpha="1.0"  android:toAlpha="0.1"
表示图片从不透明(1.0)到透明(0.1)

android:fromAlpha="0.1"  android:toAlpha="1.0"
表示图片从透明(0.1)到不透明(1.0)

源码下载

推荐参考:

ViewFlipper

GestureDetector.OnDoubleTapListener

GestureDetector.OnGestureListener

 

Android 滑动效果入门篇(二)—— Gallery

分类: Android 8316人阅读 评论(9) 收藏 举报

Gallery 是Android官方提供的一个View容器类,继承于AbsSpinner类,用于实现页面滑动效果。

从上面的继承关系可以看出,AbsSpinner类继承自AdapterView,因此我们可以自定义实现Adapter,来填充Gallery容器的数据。

本示例通过自己实现一个Adapter,来填充Gallery容器的图片数据,首先看效果:


Activity

  1. import android.app.Activity;  
  2. import android.os.Bundle;  
  3. import android.view.View;  
  4. import android.widget.AdapterView;  
  5. import android.widget.Gallery;  
  6. import android.widget.Toast;  
  7.   
  8. public class GalleryActivity extends Activity {  
  9.     @Override  
  10.     public void onCreate(Bundle savedInstanceState) {  
  11.         super.onCreate(savedInstanceState);  
  12.         setContentView(R.layout.main);  
  13.   
  14.         Gallery gallery = (Gallery) findViewById(R.id.gallery);  
  15.         gallery.setAdapter(new ImageAdapter(this));         // gallery添加ImageAdapter图片资源  
  16.         gallery.setOnItemClickListener(listener);           // gallery设置点击图片资源的事件  
  17.     }  
  18.       
  19.     AdapterView.OnItemClickListener listener = new AdapterView.OnItemClickListener() {  
  20.         @Override  
  21.         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {  
  22.             Toast.makeText(GalleryActivity.this"图片 " + (position + 1), Toast.LENGTH_SHORT).show();  
  23.         }  
  24.     };  
  25. }  

ImageAdapter.java

  1. import android.content.Context;  
  2. import android.view.View;  
  3. import android.view.ViewGroup;  
  4. import android.widget.BaseAdapter;  
  5. import android.widget.Gallery;  
  6. import android.widget.ImageView;  
  7.   
  8. public class ImageAdapter extends BaseAdapter {  
  9.     private Context mContext;  
  10.       
  11.     //  图片数组源  
  12.     private Integer[] imgs = { R.drawable.img1, R.drawable.img2,  
  13.             R.drawable.img3, R.drawable.img4, R.drawable.img5,  
  14.             R.drawable.img6, R.drawable.img7};  
  15.   
  16.     public ImageAdapter(Context c) {  
  17.         mContext = c;  
  18.     }  
  19.   
  20.     @Override  
  21.     public int getCount() {  
  22.         return imgs.length;  
  23.     }  
  24.   
  25.     // 获取图片位置  
  26.     @Override  
  27.     public Object getItem(int position) {  
  28.         return imgs[position];  
  29.     }  
  30.   
  31.     // 获取图片ID  
  32.     @Override  
  33.     public long getItemId(int position) {  
  34.         return position;  
  35.     }  
  36.   
  37.     @Override  
  38.     public View getView(int position, View convertView, ViewGroup parent) {  
  39.         ImageView imageview = new ImageView(mContext);  
  40.   
  41.         imageview.setImageResource(imgs[position]);  
  42.         imageview.setLayoutParams(new Gallery.LayoutParams(240120));      // 设置布局 图片120×120显示  
  43.         imageview.setScaleType(ImageView.ScaleType.CENTER);             // 设置显示比例类型(不缩放)  
  44.         return imageview;  
  45.     }  
  46. }  

main.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="wrap_content"  
  5.     android:orientation="vertical">  
  6.       
  7.     <!-- 文字居中显示  android:gravity -->  
  8.     <!-- 控件居中显示  android:layout_gravity -->  
  9.       
  10.     <TextView   
  11.         android:id="@+id/tv"  
  12.         android:layout_width="fill_parent"  
  13.         android:layout_height="wrap_content"  
  14.         android:gravity="center"                  
  15.         android:layout_gravity="center"           
  16.         android:layout_marginTop="50dip"  
  17.         android:textColor="#ffff0000"  
  18.         android:textSize="30sp"  
  19.         android:text="Gallery Test"/>  
  20.       
  21.     <Gallery   
  22.     android:id="@+id/gallery"  
  23.     android:layout_width="fill_parent"  
  24.     android:layout_height="wrap_content"  
  25.     android:layout_marginTop="10dip"  
  26.     android:layout_below="@id/tv" />  
  27.       
  28. </RelativeLayout>  

源码下载

示例分析:

1、ImageAdapter中,是继承BaseAdapter自定义实现的,重载了BaseAdapter的几个虚方法,其中重要的有两个

getCount()
: 返回资源的大小(总长度)

getView(int position, View convertView, ViewGroup parent): 返回当前显示的资源(获取焦点)

2、GalleryActivity中,通过gallery.setAdapter(imgAdapter); 设置资源,然后设置gallery的点击监听事件。

3、main.xml中,是布局文件,显示一个TextView和Gallery,RelativeLayout是相对布局

总体来说,Gallery 上述的示例很简单,结构比较清晰,能够满足基本的应用。

Gallery 高级应用

上面的示例,仅仅是最简单的Gallery应用,如果我们想做的更酷、更炫的效果:

1、Gallery 图片显示能够循环播放,即向右滑到左侧第一张图片后,需要接着显示最后一张图片;向左滑到最后一张图片后,需要接着显示第一张图片,往复不间断显示的循环效果。

2、选中图片高亮,未选中图片阴影,更加突出当前获取焦点的选中图片

3、区分“点击”与“选中”图片事件的区别和适用场景

效果1 —— 高亮显示

没有选中,在GalleryActivity中,设置gallery.setUnselectedAlpha(0.3f); 透明度为0.3

选中,在ImageAdapter的getView(int position, View convertView, ViewGroup parent)中,设置imageview.setBackgroundColor(Color.alpha(1)); 背景色为1

效果2 —— 循环播放

原理:Gallery循环播放的原理,跟循环链表的思想一样,首尾item连接都是通过“取余”实现

修改1、ImageAdapter中的getCount() 方法中,修改返回值为无穷大 return Integer.MAX_VALUE;

修改2、ImageAdapter中的getView(int position, View convertView, ViewGroup parent)方法中,设置imageview.setImageResource(imgs[position % imgs.length]); 取余

修改3、GalleryActivity中,设置gallery.setSelection(imgAdapter.imgs.length * 100); 使gallery显示图片的位置从中间开始显示(即imgAdapter.imgs.length * 100)

修改解释:

修改1,主要是为了是循环接近无限往复循环,使position无限大,循环在实践应用上不容易结束(理论上会结束,即2^31-1约20亿次循环后)

修改2,通过取余,使图片能够重复利用并显示

修改3,由于起始位置如果是0,则向右滑动左侧将无法循环(此时左侧将为-1,超出了imgs[]数组的下边界),因此开始应设置起始位置为imgAdapter.imgs.length的整数倍

效果3 —— “点击”和“选中”事件

1、点击事件OnItemClickListener,是需要用手点击才触发,滑动时不触发

2、选中事件OnItemSelectedListener,是当图片滑到屏幕正中,则视为自动选中,在滑动的过程中会触发

适用场景:

1、点击事件OnItemClickListener,是在确定要选中该项时,才点击进行逻辑处理

2、选中事件OnItemSelectedListener,可以用来提醒用户,当前获取焦点的项,如果确认为该项则需要点击OnItemClickListener后,进行下一步的逻辑处理

高级应用完整代码:

Activity

  1. import android.app.Activity;  
  2. import android.os.Bundle;  
  3. import android.view.Gravity;  
  4. import android.view.View;  
  5. import android.widget.AdapterView;  
  6. import android.widget.Gallery;  
  7. import android.widget.Toast;  
  8.   
  9. public class GalleryActivity extends Activity {  
  10.       
  11.     private ImageAdapter imgAdapter = null;         // 声明图片资源对象  
  12.     private Gallery gallery = null;  
  13.       
  14.     @Override  
  15.     public void onCreate(Bundle savedInstanceState) {  
  16.         super.onCreate(savedInstanceState);  
  17.         setContentView(R.layout.main);  
  18.   
  19.         gallery = (Gallery) findViewById(R.id.gallery);  
  20.         imgAdapter = new ImageAdapter(this);  
  21.         gallery.setAdapter(imgAdapter);                     // 设置图片资源  
  22.         gallery.setGravity(Gravity.CENTER_HORIZONTAL);      // 设置水平居中显示  
  23.         gallery.setSelection(imgAdapter.imgs.length * 100);     // 设置起始图片显示位置(可以用来制作gallery循环显示效果)  
  24.           
  25.         gallery.setOnItemClickListener(clickListener);          // 设置点击图片的监听事件(需要用手点击才触发,滑动时不触发)  
  26.         gallery.setOnItemSelectedListener(selectedListener);        // 设置选中图片的监听事件(当图片滑到屏幕正中,则视为自动选中)  
  27.         gallery.setUnselectedAlpha(0.3f);                   // 设置未选中图片的透明度  
  28.         gallery.setSpacing(40);                         // 设置图片之间的间距  
  29.     }  
  30.       
  31.     // 点击图片的监听事件  
  32.     AdapterView.OnItemClickListener clickListener = new AdapterView.OnItemClickListener() {  
  33.         @Override  
  34.         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {  
  35.             Toast.makeText(GalleryActivity.this"点击图片 " + (position + 1), 100).show();  
  36.         }  
  37.     };  
  38.       
  39.     // 选中图片的监听事件  
  40.     AdapterView.OnItemSelectedListener selectedListener = new AdapterView.OnItemSelectedListener() {  
  41.         @Override  
  42.         public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {  
  43.             Toast.makeText(GalleryActivity.this"选中图片 " + (position + 1), 20).show();  
  44.         }  
  45.   
  46.         @Override  
  47.         public void onNothingSelected(AdapterView<?> arg0) {  
  48.               
  49.         }  
  50.     };  
  51. }  

ImageAdapter.java

  1. import android.content.Context;  
  2. import android.graphics.Color;  
  3. import android.view.View;  
  4. import android.view.ViewGroup;  
  5. import android.widget.BaseAdapter;  
  6. import android.widget.Gallery;  
  7. import android.widget.ImageView;  
  8.   
  9. public class ImageAdapter extends BaseAdapter {  
  10.     private Context mContext;  
  11.       
  12.     //  图片数组源  
  13.     public Integer[] imgs = { R.drawable.img1, R.drawable.img2,  
  14.             R.drawable.img3, R.drawable.img4, R.drawable.img5,  
  15.             R.drawable.img6, R.drawable.img7};  
  16.   
  17.     public ImageAdapter(Context c) {  
  18.         mContext = c;  
  19.     }  
  20.   
  21.     @Override  
  22.     public int getCount() {  
  23.         return Integer.MAX_VALUE;  
  24.     }  
  25.   
  26.     // 获取图片位置  
  27.     @Override  
  28.     public Object getItem(int position) {  
  29.         return imgs[position];  
  30.     }  
  31.   
  32.     // 获取图片ID  
  33.     @Override  
  34.     public long getItemId(int position) {  
  35.         return position;  
  36.     }  
  37.   
  38.     @Override  
  39.     public View getView(int position, View convertView, ViewGroup parent) {  
  40.         ImageView imageview = new ImageView(mContext);  
  41.   
  42.         imageview.setImageResource(imgs[position % imgs.length]);  
  43.         imageview.setLayoutParams(new Gallery.LayoutParams(20094));       // 设置布局 图片120×120显示  
  44.         imageview.setScaleType(ImageView.ScaleType.CENTER);             // 设置显示比例类型  
  45.         imageview.setBackgroundColor(Color.alpha(1));  
  46.         return imageview;  
  47.     }  
  48. }  

源码下载

参考推荐:

Gallery

Gallery tutorial

 

Android 滑动效果基础篇(三)—— Gallery仿图像集浏览

分类: Android 7573人阅读 评论(13) 收藏 举报

Android系统自带一个Gallery浏览图片的应用,通过手指拖动时能够非常流畅的显示图片,用户交互和体验都很好。

本示例就是通过Gallery和自定义的View,模仿实现一个仿Gallery图像集的图片浏览效果。效果图如下:

1、基本原理

在 Activity 中实现 OnGestureListener 的接口 onFling() 手势事件,通过自定义的 View 绘制draw() 图片

2、Activity

Activity中,通过onTouchEvent() 注册 myGesture.onTouchEvent(event)

  1. @Override  
  2. public boolean onTouchEvent(MotionEvent event) {  
  3.     switch (event.getAction()) {  
  4.     case MotionEvent.ACTION_UP:  
  5.         flingView.onFling(0);           // 手指抬起后,重置滑动距离offsetX = 0  
  6.         break;  
  7.     }  
  8.   
  9.     return myGesture.onTouchEvent(event);  
  10. }  

接着实现接口OnGestureListener 的 onScroll()方法,给继承自View的 FlingView 的handleScroll()成员方法传递滑动参数,获取滑动的x轴距离

  1. @Override  
  2. public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {  
  3.     flingView.handleScroll(-1 * (int) distanceX);  
  4.     return true;  
  5. }  

接着实现接口OnGestureListener 的 OnFling()方法,给继承自View的 FlingView 的onFling()成员方法传递滑动参数,获取手势的速度

  1. @Override  
  2. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {  
  3.     flingView.onFling((int) - velocityX);  
  4.     return true;  
  5. }  

3、FlingView

FlingView中,获取来自Activity中的手势速度

  1. public void onFling(int paramFloat1) {  
  2.     if (offsetX > GalleryDemoActivity.deviceScreenWidth / 5) {  
  3.         if (fBitmap != null) {  
  4.             isFling = true;  
  5.             isFlingRight = true;  
  6.         }  
  7.     } else if (offsetX < -GalleryDemoActivity.deviceScreenWidth / 5) {  
  8.         if (nBitmap != null) {  
  9.             isFling = true;  
  10.             isFlingLeft = true;  
  11.         }  
  12.     }  
  13.     // 开始动画效果  
  14.     startAnimation(new MyAnimation());  
  15. }  

在滑动过程中,通过实现View的Draw()方法绘制图片,注意:此时需要同时绘制当前图片(获取焦点)和下一张图片(即将获取焦点)共两张图片

  1. @Override  
  2. public void draw(Canvas canvas) {  
  3.     Paint paint = new Paint();  
  4.     Rect rect = new Rect();  
  5.     canvas.drawColor(Color.BLACK);  
  6.   
  7.     // 绘制当前图片  
  8.     if (bitmap != null) {  
  9.         int left = offsetX;  
  10.         int top = offsetY;  
  11.         int right = offsetX + GalleryDemoActivity.deviceScreenWidth;  
  12.         int bottom = offsetY + GalleryDemoActivity.deviceScreenHeight;  
  13.         rect.set(left, top, right, bottom);  
  14.         canvas.drawBitmap(bitmap, null, rect, paint);  
  15.     }  
  16.       
  17.     // 绘制下一张图片  
  18.     if (offsetX < 0) {           // 向左滑动  
  19.         if (nBitmap != null) {  
  20.             int left = GalleryDemoActivity.deviceScreenWidth + 15 + offsetX;  
  21.             int top = 0;  
  22.             int right = left + GalleryDemoActivity.deviceScreenWidth;  
  23.             int bottom = GalleryDemoActivity.deviceScreenHeight;  
  24.             rect.set(left, top, right, bottom);  
  25.             canvas.drawBitmap(nBitmap, null, rect, paint);  
  26.         }  
  27.     } else if (offsetX > 0) {        // 向右滑动  
  28.         if (fBitmap != null) {  
  29.             int left = -GalleryDemoActivity.deviceScreenWidth - 15 + offsetX;  
  30.             int top = 0;  
  31.             int right = left + GalleryDemoActivity.deviceScreenWidth;  
  32.             int bottom = GalleryDemoActivity.deviceScreenHeight;  
  33.             rect.set(left, top, right, bottom);  
  34.             canvas.drawBitmap(fBitmap, null, rect, paint);  
  35.         }  
  36.     }  
  37. }  

在滑动图片结束后,需要做滑动动画后的处理,重新设置当前图片和当前图片的上一张和下一张的状态,为下次滑动做准备

  1. @Override  
  2. protected void onAnimationEnd() {  
  3.     if (isFlingRight) {         // 向右滑动,position减1  
  4.         nBitmap = bitmap;  
  5.         bitmap = fBitmap;  
  6.         fBitmap = null;  
  7.         postion = postion - 1;  
  8.     } else if (isFlingLeft) {       // 向左滑动,position加1  
  9.         fBitmap = bitmap;  
  10.         bitmap = nBitmap;  
  11.         nBitmap = null;  
  12.         postion = postion + 1;  
  13.     }  
  14.       
  15.     isFlingRight = false;             
  16.     isFlingLeft = false;  
  17.     isFling = false;  
  18.     offsetX = 0;  
  19.     if (fBitmap == null && offsetX == 0) {          // 如果前一张图片为空(向右滑),则重置前一张图片(position - 1)  
  20.         if (postion > 0) {  
  21.             fBitmap = getBitmap(postion - 1);  
  22.         }  
  23.   
  24.     } else if (nBitmap == null && offsetX == 0) {       // 如果后一张图片为空(向左滑),则重置后一张图片(position + 1)  
  25.         if (postion < bitmaps.length - 1) {  
  26.             nBitmap = getBitmap(postion + 1);  
  27.         }  
  28.     }  
  29.     clearAnimation();             
  30. }  

4、手势坐标介绍

本示例中,用到了OnGestureListener接口的onScroll()和OnFling()方法,涉及到了Android系统坐标及触摸MotionEvent e1和e2、速度velocityX、velocityY等值

Android屏幕坐标系如下图(左)

(1)MotionEvent中 e1是手指第一次按上屏幕的起点,e2是抬起手指离开屏幕的终点,根据上图Android屏幕坐标系可知:

手指向滑动,终点(e2)在起点(e1)的右侧,有e2.getX() - e1.getX() 大于0
手指向左滑动,终点(e2)在起点(e1)的左侧,有e2.getX() - e1.getX() 小于0
手指向滑动,终点(e2)在起点(e1)的下侧,有e2.getY() - e1.getY() 大于0
手指向上滑动,终点(e2)在起点(e1)的上侧,有e2.getY() - e1.getY() 小于0

(2)onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)

distanceX,是前后两次call的X距离,不是e2与e1的水平距离

distanceX,是前后两次call的Y距离,不是e2与e1的垂直距离

具体数值的方向,请详见上图(中)

(3)onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) 

velocityX,是X轴的每秒速度

velocityY,是Y轴的每秒速度

具体数值的方向,请详见上图(右)

仔细观察可以发现:velocityX、velocityY的方向与distanceX、distanceY方向正好相反

更多OnGestureListener接口函数介绍,请见上一篇博客 Android 滑动效果入门篇(一)—— ViewFlipper

示例源码下载

Android 滑动效果基础篇(四)—— Gallery + GridView

分类: Android 5214人阅读 评论(4) 收藏 举报

Android系统自带一个GridView和Gallery两个控件,GridView网格显示,Gallery单个浏览,两者结合起来可以真正实现Gallery浏览图片效果。

本示例通过GridView和Gallery两个控件,模仿实现一个完整的仿Gallery图像集的图片浏览效果。效果图如下:

1、GridView

首先,自定义一个GridImageAdapter图片适配器,用于填充GridView控件的图片

  1. public class GridImageAdapter extends BaseAdapter {  
  2.     private Context mContext;  
  3.     Drawable btnDrawable;  
  4.   
  5.     public GridImageAdapter(Context context) {  
  6.         mContext = context;  
  7.         Resources resources = context.getResources();  
  8.         btnDrawable = resources.getDrawable(R.drawable.bg);  
  9.     }  
  10.   
  11.     @Override  
  12.     public int getCount() {  
  13.         return ImageSource.mThumbIds.length;  
  14.     }  
  15.   
  16.     @Override  
  17.     public Object getItem(int position) {  
  18.         return position;  
  19.     }  
  20.   
  21.     @Override  
  22.     public long getItemId(int position) {  
  23.         return position;  
  24.     }  
  25.   
  26.     @Override  
  27.     public View getView(int position, View convertView, ViewGroup parent) {  
  28.         ImageViewExt imageView;  
  29.         int space;  
  30.   
  31.         if (convertView == null) {  
  32.             imageView = new ImageViewExt(mContext);  
  33.             if (imageCol == 5) {  
  34.                 space = dm.heightPixels / imageCol - 6;  
  35.                 imageView.setLayoutParams(new GridView.LayoutParams(space, space));  
  36.             } else {  
  37.                 space = dm.widthPixels / imageCol - 6;  
  38.                 imageView.setLayoutParams(new GridView.LayoutParams( space, space));  
  39.             }  
  40.             imageView.setAdjustViewBounds(true);  
  41.             imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);    // 缩放图片使其长和宽一样  
  42.   
  43.              imageView.setPadding(3333);  
  44.         } else {  
  45.             imageView = (ImageViewExt) convertView;  
  46.         }  
  47.         imageView.setImageResource(ImageSource.mThumbIds[position]);  
  48.   
  49.         return imageView;  
  50.     }  
  51. }  

然后,用GridImageAdapter填充GridView

  1. gridView = (GridView) findViewById(R.id.myGrid);  
  2. gridImageAdapter = new GridImageAdapter(this);  
  3. gridView.setAdapter(gridImageAdapter);  
  4. gridView.setOnItemClickListener(listener); // 设置点击监听事件  

最后,设置GridView控件的点击监听事件

  1. AdapterView.OnItemClickListener listener = new AdapterView.OnItemClickListener() {  
  2.     @Override  
  3.     public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id) {  
  4.         Intent intent = new Intent();  
  5.         intent.setClass(GridViewActivity.this, GalleryActivity.class);  
  6.         intent.putExtra("position", position);  
  7.         startActivity(intent);  
  8.     }  
  9. };  

2、Gallery

完成了GridView的图片显示、监听事件后,现在点击图片,会启动一个Activity来显示当前点击的图片,此时显示图片的控件便是Gallery

首先,同GridView一样,自定义一个ImageAdapter图片适配器,用来填充Gallery

  1. public class ImageAdapter extends BaseAdapter {  
  2.     private Context mContext;   
  3.     private int mPos;  
  4.   
  5.     public ImageAdapter(Context context) {  
  6.         mContext = context;  
  7.     }  
  8.   
  9.     public void setOwnposition(int ownposition) {  
  10.         this.mPos = ownposition;  
  11.     }  
  12.   
  13.     public int getOwnposition() {  
  14.         return mPos;  
  15.     }  
  16.   
  17.     @Override  
  18.     public int getCount() {  
  19.         return ImageSource.mThumbIds.length;  
  20.     }  
  21.   
  22.     @Override  
  23.     public Object getItem(int position) {   
  24.         mPos=position;  
  25.         return position;  
  26.     }  
  27.   
  28.     @Override  
  29.     public long getItemId(int position) {  
  30.         mPos=position;   
  31.         return position;  
  32.     }  
  33.   
  34.     @Override  
  35.     public View getView(int position, View convertView, ViewGroup parent) {  
  36.         mPos=position;  
  37.         ImageView imageview = new ImageView(mContext);  
  38.         imageview.setBackgroundColor(0xFF000000);  
  39.         imageview.setScaleType(ImageView.ScaleType.FIT_CENTER);  
  40.         imageview.setLayoutParams(new myGallery.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));  
  41.         imageview.setImageResource(ImageSource.mThumbIds[position]);  
  42.           
  43.         return imageview;  
  44.     }  
  45. }  

然后,用ImageAdapter填充Gallery

  1. myGallery  galllery = (myGallery) findViewById(R.id.mygallery);  
  2. Intent intent = getIntent();  
  3. position = intent.getIntExtra("position"0);   // 获取GridViewActivity传来的图片位置position   
  4. ImageAdapter imgAdapter=new ImageAdapter(this);       
  5. galllery.setAdapter(imgAdapter);        // 设置图片ImageAdapter  
  6. galllery.setSelection(position);        // 设置当前显示图片  
  7.       
  8.     Animation an= AnimationUtils.loadAnimation(this,R.anim.scale );     // Gallery动画  
  9.     galllery.setAnimation(an);   

此时,如果细心可以注意到,我们的Gallery也是自己定义的myGallery,具体定义如下:

  1. public class myGallery extends Gallery {  
  2.     boolean isFirst = false;  
  3.     boolean isLast = false;  
  4.   
  5.     public myGallery(Context context) {  
  6.         super(context);  
  7.     }  
  8.   
  9.     public myGallery(Context context, AttributeSet paramAttributeSet) {  
  10.         super(context, paramAttributeSet);  
  11.     }  
  12.   
  13.     /** 是否向左滑动(true - 向左滑动; false - 向右滑动) */  
  14.     private boolean isScrollingLeft(MotionEvent e1, MotionEvent e2) {     
  15.         return e2.getX() > e1.getX();  
  16.     }  
  17.   
  18.     @Override  
  19.     public boolean onFling(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {  
  20.         ImageAdapter ia = (ImageAdapter) this.getAdapter();  
  21.         int p = ia.getOwnposition();    // 获取当前图片的position  
  22.         int count = ia.getCount();      // 获取全部图片的总数count  
  23.         int kEvent;  
  24.         if (isScrollingLeft(e1, e2)) {  
  25.             if (p == 0 && isFirst) {  
  26.                 Toast.makeText(this.getContext(), "已是第一页", Toast.LENGTH_SHORT).show();  
  27.             } else if (p == 0) {  
  28.                 isFirst = true;  
  29.             } else {  
  30.                 isLast = false;  
  31.             }  
  32.   
  33.             kEvent = KeyEvent.KEYCODE_DPAD_LEFT;  
  34.         } else {  
  35.             if (p == count - 1 && isLast) {  
  36.                 Toast.makeText(this.getContext(), "已到最后一页", Toast.LENGTH_SHORT).show();  
  37.             } else if (p == count - 1) {  
  38.                 isLast = true;  
  39.             } else {  
  40.                 isFirst = false;  
  41.             }  
  42.   
  43.             kEvent = KeyEvent.KEYCODE_DPAD_RIGHT;  
  44.         }  
  45.         onKeyDown(kEvent, null);  
  46.         return true;  
  47.     }  
  48. }  

GalleryActivity的布局文件gallery.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:gravity="center"  
  6.     android:orientation="horizontal"  
  7.     android:padding="10dip" >  
  8.     <RelativeLayout  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:background="#000000"  
  12.         android:padding="2dip" >  
  13.         <com.homer.gridgallery.myGallery  
  14.             android:id="@+id/mygallery"  
  15.             android:layout_width="fill_parent"  
  16.             android:layout_height="fill_parent"  
  17.             android:spacing="16dp" />  
  18.     </RelativeLayout>  
  19. </LinearLayout>  

源码下载

参考推荐:

GridView

Grid View tutorial

Gallery

Gallery tutorial

ViewGroup

 

Android 滑动效果进阶篇(五)—— 3D旋转

分类: Android 5002人阅读 评论(4) 收藏 举报

前面介绍了利用Android自带的控件,进行滑动翻页制作效果,现在我们通过代码实现一些滑动翻页的动画效果。

Animation实现动画有两个方式:帧动画(frame-by-frame animation)和补间动画(tweened
animation

本示例通过继承Animation自定义Rotate3D,实现3D翻页效果。效果图如下:

1、Rotate3D(Animation)

首先,自定义Animation的3D动画类Rotate3D

  1. public class Rotate3D extends Animation {  
  2.     private float fromDegree;   // 旋转起始角度  
  3.     private float toDegree;     // 旋转终止角度  
  4.     private float mCenterX;     // 旋转中心x  
  5.     private float mCenterY;     // 旋转中心y  
  6.     private Camera mCamera;  
  7.   
  8.     public Rotate3D(float fromDegree, float toDegree, float centerX, float centerY) {  
  9.         this.fromDegree = fromDegree;  
  10.         this.toDegree = toDegree;  
  11.         this.mCenterX = centerX;  
  12.         this.mCenterY = centerY;  
  13.   
  14.     }  
  15.   
  16.     @Override  
  17.     public void initialize(int width, int height, int parentWidth, int parentHeight) {  
  18.         super.initialize(width, height, parentWidth, parentHeight);  
  19.         mCamera = new Camera();  
  20.     }  
  21.   
  22.     @Override  
  23.     protected void applyTransformation(float interpolatedTime, Transformation t) {  
  24.         final float FromDegree = fromDegree;  
  25.         float degrees = FromDegree + (toDegree - fromDegree) * interpolatedTime;    // 旋转角度(angle)  
  26.         final float centerX = mCenterX;  
  27.         final float centerY = mCenterY;  
  28.         final Matrix matrix = t.getMatrix();  
  29.   
  30.         if (degrees <= -76.0f) {  
  31.             degrees = -90.0f;  
  32.             mCamera.save();  
  33.             mCamera.rotateY(degrees);       // 旋转  
  34.             mCamera.getMatrix(matrix);  
  35.             mCamera.restore();  
  36.         } else if (degrees >= 76.0f) {  
  37.             degrees = 90.0f;  
  38.             mCamera.save();  
  39.             mCamera.rotateY(degrees);  
  40.             mCamera.getMatrix(matrix);  
  41.             mCamera.restore();  
  42.         } else {  
  43.             mCamera.save();  
  44.             mCamera.translate(00, centerX);       // 位移x  
  45.             mCamera.rotateY(degrees);  
  46.             mCamera.translate(00, -centerX);  
  47.             mCamera.getMatrix(matrix);  
  48.             mCamera.restore();  
  49.         }  
  50.   
  51.         matrix.preTranslate(-centerX, -centerY);  
  52.         matrix.postTranslate(centerX, centerY);  
  53.     }  
  54. }  

然后,实例化Rotate3D的旋转方向

  1. public void initAnimation() {  
  2.     // 获取旋转中心  
  3.     DisplayMetrics dm = new DisplayMetrics();  
  4.     dm = getResources().getDisplayMetrics();  
  5.     mCenterX = dm.widthPixels / 2;  
  6.     mCenterY = dm.heightPixels / 2;  
  7.       
  8.     // 定义旋转方向  
  9.     int duration = 1000;  
  10.     lQuest1Animation = new Rotate3D(0, -90, mCenterX, mCenterY);    // 下一页的【question1】旋转方向(从0度转到-90,参考系为水平方向为0度)  
  11.     lQuest1Animation.setFillAfter(true);  
  12.     lQuest1Animation.setDuration(duration);  
  13.   
  14.     lQuest2Animation = new Rotate3D(900, mCenterX, mCenterY);     // 下一页的【question2】旋转方向(从90度转到0,参考系为水平方向为0度)(起始第一题)  
  15.     lQuest2Animation.setFillAfter(true);  
  16.     lQuest2Animation.setDuration(duration);  
  17.   
  18.     rQuest1Animation = new Rotate3D(090, mCenterX, mCenterY);     // 上一页的【question1】旋转方向(从0度转到90,参考系为水平方向为0度)  
  19.     rQuest1Animation.setFillAfter(true);  
  20.     rQuest1Animation.setDuration(duration);  
  21.   
  22.     rQuest2Animation = new Rotate3D(-900, mCenterX, mCenterY);    // 上一页的【question2】旋转方向(从-90度转到0,参考系为水平方向为0度)  
  23.     rQuest2Animation.setFillAfter(true);  
  24.     rQuest2Animation.setDuration(duration);  
  25. }  

2、Activity

首先,定义两个布局文件,用于旋转的画面切换

main.xml

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:id="@+id/layout_main"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="wrap_content"              
  5.     android:orientation="vertical">  
  6.   
  7. ...  
  8.   
  9. </LinearLayout>  

next.xml

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:id="@+id/layout_next"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="wrap_content"              
  5.     android:orientation="vertical">  
  6.   
  7. ...  
  8.   
  9. </LinearLayout>  

限于篇幅,完整布局文件请详见源码 ^_^

然后,初始化两个旋转的布局文件资源

  1. private void initMain(){  
  2.        setContentView(R.layout.main);  
  3.   
  4.     layoutmain = (LinearLayout)findViewById(R.id.layout_main);  
  5.     btn_MainLast = (Button)findViewById(R.id.main_last);  
  6.     btn_MainNext = (Button)findViewById(R.id.main_next);  
  7.       
  8.     btn_MainLast.setOnClickListener(listener);  
  9.     btn_MainNext.setOnClickListener(listener);  
  10. }  
  11.   
  12. private void initNext(){  
  13.        setContentView(R.layout.next);  
  14.   
  15.     layoutnext = (LinearLayout)findViewById(R.id.layout_next);  
  16.     btn_NextLast = (Button)findViewById(R.id.next_last);  
  17.     btn_NextNext = (Button)findViewById(R.id.next_next);  
  18.       
  19.     btn_NextLast.setOnClickListener(listener);  
  20.     btn_NextNext.setOnClickListener(listener);  
  21. }  

最后,设置布局文件中的按钮监听事件,响应3D旋转动画和方向

  1. private View.OnClickListener listener = new View.OnClickListener() {  
  2.     @Override  
  3.     public void onClick(View v) {  
  4.         switch (v.getId()) {  
  5.         case R.id.main_last:    // 上一页  
  6.             layoutmain.startAnimation(lQuest1Animation);    // 当前页向左旋转(0,-90)  
  7.             initNext();  
  8.             layoutnext.startAnimation(lQuest2Animation);    // 下一页向左旋转(90, 0)  
  9.             break;  
  10.         case R.id.main_next:    // 下一页  
  11.             layoutmain.startAnimation(rQuest1Animation);    // 当前页向右旋转(0,90)  
  12.             initNext();  
  13.             layoutnext.startAnimation(rQuest2Animation);    // 下一页向右旋转(-90, 0)  
  14.             break;  
  15.         case R.id.next_last:  
  16.             layoutnext.startAnimation(lQuest1Animation);  
  17.             initMain();  
  18.             layoutmain.startAnimation(lQuest2Animation);  
  19.             break;  
  20.         case R.id.next_next:  
  21.             layoutnext.startAnimation(rQuest1Animation);  
  22.             initMain();  
  23.             layoutmain.startAnimation(rQuest2Animation);  
  24.             break;  
  25.         }  
  26.     }  
  27. };  

源码下载

参考推荐:

animation

Android 滑动效果进阶篇(六)—— 倒影效果

分类: Android 5016人阅读 评论(3) 收藏 举报

上篇介绍了使用Animation实现3D动画旋转翻页效果,现在介绍图片倒影实现,先看效果图

本示例主要通过自定义Gallery和ImageAdapter(继承自BaseAdapter)实现

1、倒影绘制

ImageAdapter继承自BaseAdapter,详细实现可见 Android 滑动效果入门篇(二)—— Gallery 这里重点介绍倒影原理及实现

倒影原理:

倒影效果是主要由原图+间距+倒影三部分组成,高度大约为原图的3/2(原图为1、倒影为1/2)

原图,就是我们看到了最开始的图片

间距,是原图与倒影之间的间隙,如:reflectionGap = 4;

倒影,是原图下半部分1/2高度,通过矩阵变换matrix.preScale(1, -1); 获取倒立图片,然后再加上线性遮罩和阴影实现

倒影实现:

  1. /** 反射倒影 */  
  2. public boolean createReflectedImages() {  
  3.     final int reflectionGap = 4;  
  4.     int index = 0;  
  5.     for (Map<String, Object> map : list) {  
  6.         Integer id = (Integer) map.get("image");  
  7.         Bitmap originalImage = BitmapFactory.decodeResource(mContext.getResources(), id);   // 获取原始图片  
  8.         int width = originalImage.getWidth();  
  9.         int height = originalImage.getHeight();  
  10.   
  11.         Matrix matrix = new Matrix();  
  12.         matrix.preScale(1, -1);         // 图片矩阵变换(从低部向顶部的倒影)  
  13.         Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height/2, width, height/2, matrix, false);   // 截取原图下半部分  
  14.         Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height / 2), Config.ARGB_8888);          // 创建倒影图片(高度为原图3/2)  
  15.   
  16.         Canvas canvas = new Canvas(bitmapWithReflection);   // 绘制倒影图(原图 + 间距 + 倒影)  
  17.         canvas.drawBitmap(originalImage, 00null);       // 绘制原图  
  18.         Paint paint = new Paint();  
  19.         canvas.drawRect(0, height, width, height + reflectionGap, paint);       // 绘制原图与倒影的间距  
  20.         canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);    // 绘制倒影图  
  21.   
  22.         paint = new Paint();  
  23.         LinearGradient shader = new LinearGradient(0, originalImage.getHeight(), 0, bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff0x00ffffff, TileMode.CLAMP);  
  24.         paint.setShader(shader);    // 线性渐变效果  
  25.         paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));     // 倒影遮罩效果  
  26.         canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint);     // 绘制倒影的阴影效果  
  27.   
  28.         ImageView imageView = new ImageView(mContext);  
  29.         imageView.setImageBitmap(bitmapWithReflection);     // 设置倒影图片  
  30.         imageView.setLayoutParams(new myGallery.LayoutParams(180240));  
  31.         imageView.setScaleType(ScaleType.MATRIX);  
  32.         mImages[index++] = imageView;  
  33.     }  
  34.     return true;  
  35. }  

2、myGallery

自定义Gallery来实现倒影图片的浏览与选择

  1. public class myGallery extends Gallery {  
  2.   
  3.     private Camera mCamera = new Camera();  
  4.     private int mMaxRotationAngle = 60;     // 最大旋转角度 60  
  5.     private int mMaxZoom = -120;  
  6.     private int mCoveflowCenter;  
  7.   
  8.     public myGallery(Context context) {  
  9.         super(context);  
  10.         this.setStaticTransformationsEnabled(true);  
  11.     }  
  12.   
  13.     public myGallery(Context context, AttributeSet attrs) {  
  14.         super(context, attrs);  
  15.         this.setStaticTransformationsEnabled(true);  
  16.     }  
  17.   
  18.     public myGallery(Context context, AttributeSet attrs, int defStyle) {  
  19.         super(context, attrs, defStyle);  
  20.         this.setStaticTransformationsEnabled(true);  
  21.     }  
  22.   
  23.     public int getMaxRotationAngle() {  
  24.         return mMaxRotationAngle;  
  25.     }  
  26.   
  27.     public void setMaxRotationAngle(int maxRotationAngle) {  
  28.         mMaxRotationAngle = maxRotationAngle;  
  29.     }  
  30.   
  31.     public int getMaxZoom() {  
  32.         return mMaxZoom;  
  33.     }  
  34.   
  35.     public void setMaxZoom(int maxZoom) {  
  36.         mMaxZoom = maxZoom;  
  37.     }  
  38.   
  39.     /** 获取Gallery的中心x */  
  40.     private int getCenterOfCoverflow() {  
  41.         return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft();  
  42.     }  
  43.   
  44.     /** 获取View的中心x */  
  45.     private static int getCenterOfView(View view) {  
  46.         return view.getLeft() + view.getWidth() / 2;  
  47.     }  
  48.   
  49.     @Override  
  50.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  51.         mCoveflowCenter = getCenterOfCoverflow();  
  52.         super.onSizeChanged(w, h, oldw, oldh);  
  53.     }  
  54.   
  55.     @Override  
  56.     protected boolean getChildStaticTransformation(View child, Transformation trans) {  
  57.         final int childCenter = getCenterOfView(child);  
  58.         final int childWidth = child.getWidth();  
  59.         int rotationAngle = 0;  
  60.   
  61.         trans.clear();  
  62.         trans.setTransformationType(Transformation.TYPE_BOTH);      // alpha 和 matrix 都变换  
  63.   
  64.         if (childCenter == mCoveflowCenter) {   // 正中间的childView  
  65.             transformImageBitmap((ImageView) child, trans, 0);    
  66.         } else {        // 两侧的childView  
  67.             rotationAngle = (int) ( ( (float) (mCoveflowCenter - childCenter) / childWidth ) * mMaxRotationAngle );  
  68.             if (Math.abs(rotationAngle) > mMaxRotationAngle) {  
  69.                 rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle : mMaxRotationAngle;  
  70.             }  
  71.             transformImageBitmap((ImageView) child, trans, rotationAngle);  
  72.         }  
  73.   
  74.         return true;  
  75.     }  
  76.   
  77.     private void transformImageBitmap(ImageView child, Transformation trans, int rotationAngle) {  
  78.         mCamera.save();  
  79.           
  80.         final Matrix imageMatrix = trans.getMatrix();  
  81.         final int imageHeight = child.getLayoutParams().height;  
  82.         final int imageWidth = child.getLayoutParams().width;  
  83.         final int rotation = Math.abs(rotationAngle);  
  84.   
  85.         // 在Z轴上正向移动camera的视角,实际效果为放大图片; 如果在Y轴上移动,则图片上下移动; X轴上对应图片左右移动。  
  86.         mCamera.translate(0.0f, 0.0f, 100.0f);  
  87.   
  88.         // As the angle of the view gets less, zoom in  
  89.         if (rotation < mMaxRotationAngle) {  
  90.             float zoomAmount = (float) (mMaxZoom + (rotation * 1.5));  
  91.             mCamera.translate(0.0f, 0.0f, zoomAmount);  
  92.         }  
  93.   
  94.         mCamera.rotateY(rotationAngle);     // rotationAngle 为正,沿y轴向内旋转; 为负,沿y轴向外旋转  
  95.           
  96.         mCamera.getMatrix(imageMatrix);  
  97.         imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));  
  98.         imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));  
  99.           
  100.         mCamera.restore();  
  101.     }  
  102. }  

3、Activity

Activity中,主要实现自定义Gallery的图片填充ImageAdapter、myGallery选择事件监听、点击事件监听

  1. private void initRes(){  
  2.     tvTitle = (TextView) findViewById(R.id.tvTitle);  
  3.     gallery = (myGallery) findViewById(R.id.mygallery);     // 获取自定义的myGallery控件  
  4.   
  5.     adapter = new ImageAdapter(this);     
  6.     adapter.createReflectedImages();    // 创建倒影效果  
  7.     gallery.setAdapter(adapter);  
  8.       
  9.     gallery.setOnItemSelectedListener(new OnItemSelectedListener() {    // 设置选择事件监听  
  10.         @Override  
  11.         public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {  
  12.             tvTitle.setText(adapter.titles[position]);  
  13.         }  
  14.   
  15.         @Override  
  16.         public void onNothingSelected(AdapterView<?> parent) {  
  17.         }  
  18.     });  
  19.   
  20.     gallery.setOnItemClickListener(new OnItemClickListener() {          // 设置点击事件监听  
  21.         @Override  
  22.         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {  
  23.             Toast.makeText(Main.this"img " + (position+1) + " selected", Toast.LENGTH_SHORT).show();  
  24.         }  
  25.     });  
  26. }  

main.xml布局文件中,通过实现自定义的myGallery,来显示图片集合

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.     <TextView  
  7.         android:id="@+id/tvTitle"  
  8.         android:layout_width="wrap_content"  
  9.         android:layout_height="wrap_content"  
  10.         android:layout_centerHorizontal="true"  
  11.         android:textSize="16sp" />  
  12.       
  13.     <com.homer.reflect.myGallery  
  14.         android:id="@+id/mygallery"  
  15.         android:layout_width="fill_parent"  
  16.         android:layout_height="wrap_content"  
  17.         android:layout_below="@id/tvTitle"  
  18.         android:layout_marginTop="10dip" />  
  19. </RelativeLayout>  

源码下载

抱歉!评论已关闭.