利用Viewpager实现真正的Gallery, 左右条目可点击居中, 左右条目可滑动,也就是可以获得焦点
前言: 最近公司做一个想Gallery一样的效果,于是想在网上找个现成的,但是怎么找也没找到,就是找到一个鸿洋大神的巧用ViewPager 打造不一样的广告轮播切换效果的,但是左右条目不可点击,又不可以滑动,所以就做了个这个控件,本控件也是利用ViewPgaer做出来的
首先看一下效果:
从图上可以看出,
两边的item可以被点击居中, 可以被滑动,也就是可以获得焦点
接下来讲一下原理
设置PageTransformer
PageTransformer大家估计都相当熟悉吧,下面是PageTransformer的代码
public void transformPage(View page, float position) {
if (position < -1) {
position = -1;
} else if (position > 1) {
position = 1;
}
float tempScale = position < 0 ? 1 + position : 1 - position;
float slope = (MAX_SCALE - MIN_SCALE) / 1;
float scaleValue = MIN_SCALE + tempScale * slope;
page.setScaleX(scaleValue);
page.setScaleY(scaleValue);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
page.getParent().requestLayout();
}
}
这个的作用就是让Viewpager展示多个条目,看下面的图片, 红色的才是ViewPager的大小,所以此PageTransformer是为了在Viewpager外面展示图片
大家都知道PageTransformer没有真正的改变viewpager的状态,但是为什么本demo中两侧的图片可以点击还可以滑动呢,让我慢慢跟你道来
其实很简单,就是在viewpage的父控件中拦截dispatchTouchEvent的方法来控制ViewPgaer的滑动和相应点击事件的(不懂dispatchTouchEvent即事件分发的, 问问度娘就可以了)
先看一下自定义ViewPager
public class GalleryViewPager extends ViewPager {
//默认距离
private final static float DISTANCE = 10;
private float downX;
private float downY;
public GalleryViewPager(Context context) {
super(context);
}
public GalleryViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN){
downX = ev.getX();
downY = ev.getY();
}else if (ev.getAction() == MotionEvent.ACTION_UP) {
float upX = ev.getX();
float upY = ev.getY();
if(Math.abs(upX - downX) > DISTANCE || Math.abs(upY - downY) > DISTANCE){
return super.dispatchTouchEvent(ev);
}
View view = viewOfClickOnScreen(ev);
if (view != null) {
int index = (Integer) view.getTag();
if (getCurrentItem() != index) {
setCurrentItem(index);
}
}
}
return super.dispatchTouchEvent(ev);
}
private View viewOfClickOnScreen(MotionEvent ev) {
int childCount = getChildCount();
int currentIndex = getCurrentItem();
int[] location = new int[2];
for (int i = 0; i < childCount; i++) {
View v = getChildAt(i);
int position = (Integer) v.getTag();
v.getLocationOnScreen(location);
int minX = location[0];
int minY = location[1];
int maxX = location[0] + v.getWidth();
int maxY = location[1] + v.getHeight();
if(position < currentIndex){
maxX -= v.getWidth() * (1 - ScalePageTransformer.MIN_SCALE) * 0.5 + v.getWidth() * (Math.abs(1 - ScalePageTransformer.MAX_SCALE)) * 0.5;
minX -= v.getWidth() * (1 - ScalePageTransformer.MIN_SCALE) * 0.5 + v.getWidth() * (Math.abs(1 - ScalePageTransformer.MAX_SCALE)) * 0.5;
}else if(position == currentIndex){
minX += v.getWidth() * (Math.abs(1 - ScalePageTransformer.MAX_SCALE));
}else if(position > currentIndex){
maxX -= v.getWidth() * (Math.abs(1 - ScalePageTransformer.MAX_SCALE)) * 0.5;
minX -= v.getWidth() * (Math.abs(1 - ScalePageTransformer.MAX_SCALE)) * 0.5;
}
float x = ev.getRawX();
float y = ev.getRawY();
if ((x > minX && x < maxX) && (y > minY && y < maxY)) {
return v;
}
}
return null;
}
}
等会让VIewPgaer的父控件的dispatchTouchEvent实现此ViewPager的dispatchTouchEvent就可以了 比如在mainactivity中找到父控件, 根据父控件的dispatchTouchEvent来控制此ViewPgaer的变化
findViewById(R.id.root).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return mViewPager.dispatchTouchEvent(event);
}
});
控制两边图片的点击事件只响应居中图片, 不响应点击事件
在viewpageradapter中设置如下代码, 中间的图片永远是mViewPager.getCurrentItem(),只有中间的图片可以响应点击事件
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("wwwwwwwwss", mViewPager.getCurrentItem()+ "------" + position);
if ((mViewPager.getCurrentItem() ) == position) {
Toast.makeText(mContext, "点击的位置是:::"+position, Toast.LENGTH_SHORT).show();
}
}
});
使用方式
添加依赖
1.在根目录的build.gradle中添加
allprojects {
repositories {
jcenter()
maven { url 'https://jitpack.io' }// 添加jitpack.依赖
}
2.在app的build.gradle中添加
compile 'com.github.niezhiyang:NGallery:v1.0.1'
在使用的xml中,用一个父控件包裹住GalleryViewPager,目的是为了控制父控件的滑动,和点击,来设置Viewpager的状态
<RelativeLayout
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
>
<!--是否限制子View在其范围内,我们将其值设置为false后那么当子控件的高度高于父控件时也会完全显示,而不会被压缩-->
<com.nie.ngallerylibrary.GalleryViewPager
android:id="@+id/viewpager"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
android:clipChildren="false"
android:overScrollMode="never"/>
</RelativeLayout>
代码中设置
mViewPager = (GalleryViewPager) findViewById(R.id.viewpager);//找到这个控件
mViewPager.setPageTransformer(true, new ScalePageTransformer());//设置PageTransformer,本库只有一个ScalePageTransformer,如果这个ScalePageTransformer满足不了您的需求,您可以自己写一个PageTransformer
findViewById(R.id.root).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return mViewPager.dispatchTouchEvent(event);
}
});//找到这个父控件设置他的listener
mPagerAdapter = new SimpleAdapter(this);//初始化adapter
mViewPager.setAdapter(mPagerAdapter);//设置adapter
adapter要继承MyPageradapter;例如
public class SimpleAdapter extends MyPageradapter {
private final List<Integer> mList;
private final Context mContext;
public SimpleAdapter(Context context) {
mList = new ArrayList<>();
mContext = context;
}
public void addAll(List<Integer> list) {
mList.addAll(list);
notifyDataSetChanged();
}
@Override
public View getView(final int position, View convertView, ViewGroup container) {
ImageView imageView = null;
if (convertView == null) {
imageView = new ImageView(mContext);
} else {
imageView = (ImageView) convertView;
}
imageView.setTag(position);
imageView.setImageResource(mList.get(position));
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if ((mViewPager.getCurrentItem() ) == position) {
Toast.makeText(mContext, "点击的位置是:::"+position, Toast.LENGTH_SHORT).show();
}
}
});
return imageView;
}
@Override
public int getCount() {
return mList.size();
}
}
这只不过是简单的添加imageview,你也可以添加fragment做出绚丽的效果,还可以跟任意的viewpgaerindicator使用了,是不是想想就高兴了
具体实现请看源码:NGallery
apk位置:demo.apk
欢迎start