3D画廊
之前我都是写的学习的内容,我在写这些教程时遇到有趣的炫酷的小例子也会专门拿出来写一篇文章,今天就写一个酷炫的小例子,叫3D画廊,它是属于ViewPage的进阶版。
此项目下载地点:https://github.com/qySvip/3D-gallery
下面的指示器是使用的一大神的第三方库,会在文章下方简单讲述一下。
效果图
3D画廊的实现
首先是布局文件
1 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 xmlns:app="http://schemas.android.com/apk/res-auto" 5 android:clipChildren="false" 6 android:background="@android:color/black" 7 android:id="@+id/frame_layout"> 8 9 <android.support.v4.view.ViewPager 10 android:layout_width="match_parent" 11 android:layout_height="450dp" 12 android:layout_gravity="center" 13 android:id="@+id/gallery" 14 android:clipChildren="false" 15 android:layout_marginLeft="85dp" 16 android:layout_marginRight="85dp" /> 17 18 <com.lwj.widget.viewpagerindicator.ViewPagerIndicator 19 android:id="@+id/indicator_circle_line" 20 android:layout_width="match_parent" 21 android:layout_height="50dp" 22 android:layout_gravity="bottom" 23 android:background="#efefef" 24 app:vpi_default_color="#FF239AF5" 25 app:vpi_distance="60dp" 26 app:vpi_distanceType="BY_LAYOUT" 27 app:vpi_indicatorType="CIRCLE_LINE" 28 app:vpi_radius="10dp" 29 app:vpi_selected_color="#FF239AF5" 30 /> 31 32 </FrameLayout>
代码中的ViewPagerIndicator是用于实现下方的指示器,这个我们在最后在进行解释;
首先布局使用帧布局,内部用到ViewPager控件,注意在ViewPager控件的父布局中我们要添加android:clipChildren="false"属性,它的作用是定义它的子控件是否要在它应有的边界内进行绘制,默认值为true。我们这里要用false。
然后在ViewPager中用到android:layout_marginLeft和android:layout_marginRight属性,值不能太大,过大会导致看不到两边的图像,具体根据自己满意进行调整。高度属性为图片和倒影的加起来的高度,自己进行调整。
GalleryPageTransformer类
1 package sample.sdk.qy.com.demo; 2 3 import android.support.v4.view.ViewPager; 4 import android.view.View; 5 6 public class GalleryPageTransformer implements ViewPager.PageTransformer { 7 private static final float MAX_ROTATION=20.0f; 8 private static final float MIN_SCALE=0.75f; 9 private static final float MAX_TRANSLATE=20.0f; 10 11 @Override 12 public void transformPage(View page, float position) { 13 if(position<-1) { 14 page.setTranslationX(MAX_TRANSLATE); 15 page.setScaleX(MIN_SCALE); 16 page.setScaleY(MIN_SCALE); 17 page.setRotationY(-MAX_ROTATION); 18 } 19 else if(position<=0) { 20 page.setTranslationX(-MAX_TRANSLATE*position); 21 float scale=MIN_SCALE+(1-MIN_SCALE)*(1.0f+position); 22 page.setScaleX(scale); 23 page.setScaleY(scale); 24 page.setRotationY(MAX_ROTATION*position); 25 } 26 else if(position<=1) { 27 page.setTranslationX(-MAX_TRANSLATE*position); 28 float scale=MIN_SCALE+(1-MIN_SCALE)*(1.0f-position); 29 page.setScaleX(scale); 30 page.setScaleY(scale); 31 page.setRotationY(MAX_ROTATION*position); 32 } 33 else { 34 page.setTranslationX(-MAX_TRANSLATE); 35 page.setScaleX(MIN_SCALE); 36 page.setScaleY(MIN_SCALE); 37 page.setRotationY(MAX_ROTATION); 38 } 39 } 40 }
PageTransformer是ViewPager内部定义的接口,这个接口主要用于控制ViewPager中item view的滑动效果。
- setTranslationX 设置view相对原始位置的水平偏移量
- setScaleX 设置水平缩放的基准点
- setScaleY 设置竖直缩放的基准点
- setRotationY 设置竖直的选择的基准点
ImageUtils类
1 package sample.sdk.qy.com.demo; 2 3 import android.content.Context; 4 import android.graphics.Bitmap; 5 import android.graphics.BitmapFactory; 6 import android.graphics.Canvas; 7 import android.graphics.Color; 8 import android.graphics.LinearGradient; 9 import android.graphics.Matrix; 10 import android.graphics.Paint; 11 import android.graphics.PorterDuff; 12 import android.graphics.PorterDuffXfermode; 13 import android.graphics.Shader; 14 15 public class ImageUtils { 16 public static Bitmap getReverseBitmapById(Context context, int resId, float percent) { 17 // get the source bitmap 18 Bitmap srcBitmap= BitmapFactory.decodeResource(context.getResources(), resId); 19 // get the tow third segment of the reverse bitmap 20 Matrix matrix=new Matrix(); 21 matrix.setScale(1, -1); 22 Bitmap rvsBitmap=Bitmap.createBitmap(srcBitmap, 0, (int) (srcBitmap.getHeight()*(1-percent)), 23 srcBitmap.getWidth(), (int) (srcBitmap.getHeight()*percent), matrix, false); 24 // combine the source bitmap and the reverse bitmap 25 Bitmap comBitmap=Bitmap.createBitmap(srcBitmap.getWidth(), 26 srcBitmap.getHeight()+rvsBitmap.getHeight()+20, srcBitmap.getConfig()); 27 Canvas gCanvas=new Canvas(comBitmap); 28 gCanvas.drawBitmap(srcBitmap, 0, 0, null); 29 gCanvas.drawBitmap(rvsBitmap, 0, srcBitmap.getHeight()+20, null); 30 Paint paint=new Paint(); 31 LinearGradient shader=new LinearGradient(0, srcBitmap.getHeight()+20, 0, comBitmap.getHeight(), 32 Color.BLACK, Color.TRANSPARENT, Shader.TileMode.CLAMP); 33 paint.setShader(shader); 34 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); 35 gCanvas.drawRect(0, srcBitmap.getHeight()+20, srcBitmap.getWidth(), comBitmap.getHeight(), paint); 36 return comBitmap; 37 } 38 }
这是做图片倒影的方法,内部使用了Canvas和Bitmap类,这些后面会进行讲述。
ViewAdapter类
1 package sample.sdk.qy.com.demo; 2 3 import android.support.v4.view.PagerAdapter; 4 import android.view.View; 5 import android.view.ViewGroup; 6 7 import java.util.List; 8 9 public class ViewAdapter extends PagerAdapter { 10 private List<View> datas; 11 12 public ViewAdapter(List<View> list) { 13 datas=list; 14 } 15 16 @Override 17 public int getCount() { 18 return datas.size(); 19 } 20 21 @Override 22 public boolean isViewFromObject(View view, Object object) { 23 return view==object; 24 } 25 26 @Override 27 public Object instantiateItem(ViewGroup container, int position) { 28 View view=datas.get(position); 29 container.addView(view); 30 return view; 31 } 32 33 @Override 34 public void destroyItem(ViewGroup container, int position, Object object) { 35 container.removeView(datas.get(position)); 36 } 37 }
适配器类,这里的适配方式为图片和倒影一同进行适配。
MainActivity类
1 package sample.sdk.qy.com.demo; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.support.v4.view.PagerAdapter; 6 import android.support.v4.view.ViewPager; 7 import android.view.MotionEvent; 8 import android.view.View; 9 import android.widget.FrameLayout; 10 import android.widget.ImageView; 11 12 import com.lwj.widget.viewpagerindicator.ViewPagerIndicator; 13 14 import java.lang.reflect.Field; 15 import java.util.ArrayList; 16 import java.util.List; 17 18 public class MainActivity extends Activity { 19 20 private List<View> pages; 21 private FrameLayout layout; 22 private ViewPager pager; 23 private ViewPagerIndicator mIndicatorCircleLine; 24 25 @Override 26 protected void onCreate(Bundle savedInstanceState) { 27 super.onCreate(savedInstanceState); 28 setContentView(R.layout.activity_main); 29 30 pages=getPages(); 31 pager=(ViewPager) findViewById(R.id.gallery); 32 PagerAdapter adapter=new ViewAdapter(pages); 33 pager.setAdapter(adapter); 34 pager.setPageMargin(20); 35 pager.setOffscreenPageLimit(3); 36 pager.setPageTransformer(true, new GalleryPageTransformer()); 37 38 layout=(FrameLayout) findViewById(R.id.frame_layout); 39 layout.setOnTouchListener(new View.OnTouchListener() { 40 @Override 41 public boolean onTouch(View v, MotionEvent event) { 42 return pager.dispatchTouchEvent(event); 43 } 44 }); 45 46 mIndicatorCircleLine = (ViewPagerIndicator) findViewById(R.id.indicator_circle_line); 47 mIndicatorCircleLine.setViewPager(pager,9); 48 } 49 50 private List<View> getPages() { 51 List<View> pages=new ArrayList<>(); 52 Field[] fields=R.drawable.class.getDeclaredFields(); 53 try { 54 for (Field field : fields) { 55 if (field.getName().startsWith("page")) { 56 ImageView view = new ImageView(this); 57 view.setImageBitmap(ImageUtils.getReverseBitmapById(this, field.getInt(null), 0.5f)); 58 pages.add(view); 59 } 60 } 61 } catch (IllegalAccessException e) { 62 e.printStackTrace(); 63 } 64 return pages; 65 } 66 }
主界面方法,主要用于填充入图片、适配器适配。这里填充图片的方法为在drawable文件中查找以page开头的图片进行填充。
到这里3D画廊就完成了。。。。。。。。。
指示器
下面来说一下指示器的做法,用的是网上一大神的第三方类。
使用方法:
1、在 project的build.gradle 添加:
allprojects { repositories { ... maven { url "https://jitpack.io" } } }
dependencies {
implementation 'com.github.LinweiJ:ViewPagerIndicator:0.1.0' }
注意这里要用implementation,compile现在已被废弃,官方显示将在2018年年底删除。
3、将ViewPagerIndicator控件添加到布局文件。
属性:
app:vpi_selected_color
app:vpi_default_color
(如果 indicatorType=CIRCLE_LINE default_color 为指示器唯一颜色 ,selected_color 不起作用)app:vpi_radius
(点的大小,在indicatorType= CIRCLE_LINE 的情况下 radius 是点的高)app:vpi_length
(只作用在 indicatorType=CIRCLE_LINE 的情况下,为 指示器点的长度)app:vpi_distance
(只作用在 distanceType=BY_DISTANCE 的情况下)app:vpi_num
app:vpi_indicatorType
(LINE; CIRCLE; CIRCLE_LINE; BEZIER;SPRING)LINE:线 ; CIRCLE:圆点(默认) ; CIRCLE_LINE:圆角矩形; BEZIER:弹性球 ; SPRING: 弹簧粘性球app:vpi_distanceType
(BY_RADIUS; BY_DISTANCE ; BY_LAYOUT )BY_RADIUS:3倍radius ; BY_DISTANCE :定义固定距离 ;BY_LAYOUT :根据layout_width均分得到距离app:vpi_animation
(默认为true:动画开启 ; false:关闭动画)
4、在java文件中初始化该控件
使用mIndicatorCircleLine.setViewPager(pager,9);,第一个参数为适配器,第二个参数为图片的个数。