由于Gallery的性能问题(我至今未搞清楚是什么性能问题),Gallery已经被谷歌官方弃用(Deprecated API-16)。Google推荐了以下三种替代方案:
- 使用ViewPager 来实现,但是ViewPager 是support-v4的一个类,需要依赖support-v4
- 使用RecyclerView 来实现,但是RecyclerView 是support-v7的一个类,需要依赖support-v7
- 使用HorizontalScrollView来实现Gallery的效果
注意:HorizontalScrollView可以实现Gallery的效果,但是HorizontalScrollView存在一个很大的问题:HorizontalScrollView 并不能回收重用item view,所有的item view都是一次性加载到容器中,如果仅是用来展示少量的图片应该是没问题的,但是展示大量图片很可能OOM,厨房对HorizontalScrollView进行扩展。扩展可以详见http://blog.csdn.net/lmj623565791/article/details/38140505
下面将介绍如何使用HorizontalScrollView来实现Gallery3D的效果!
1. activity_main.xml
<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" >
<com.open.ui.horizontalScrollViewForGallery3D.MyHorizontalScrollView
android:id="@+id/my_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:scrollbars="none" >
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical" >
</LinearLayout>
</com.open.ui.horizontalScrollViewForGallery3D.MyHorizontalScrollView>
</RelativeLayout>
2. item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/id_index_gallery_item_image"
android:layout_width="150dp"
android:layout_height="150dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:layout_margin="5dp"
android:scaleType="fitXY" >
</ImageView>
3. MainActivity.java
package com.open.ui.horizontalScrollViewForGallery3D;
import com.example.mytesting.R;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyHorizontalScrollView myScrollView = (MyHorizontalScrollView) findViewById(R.id.my_scroll_view);
myScrollView.initDatas();
}
}
4. MyHorizontalScrollView.java
package com.open.ui.horizontalScrollViewForGallery3D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.example.mytesting.R;
import com.nineoldandroids.view.ViewHelper;
public class MyHorizontalScrollView extends HorizontalScrollView {
private Context mContext;
/** The item data to display on screen */
private List<Integer> mDatas;
/** The item parent layout */
private LinearLayout mContainer;
/** The screent center of HorizontalScrollView */
private int mScrollViewCenter;
/** The width of item */
private int mChildWidth;
/** The index of the initial centered displayed item */
private int mInitialDisplayedItemIndex;
/** Record the initial scrollX of the HorizontalScrollView */
private int mInitialScrollX;
/** A flag indicate which direction the HorizontalScrollView moved */
private boolean mIsScrollRight = false;
public MyHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mDatas = new ArrayList<Integer>(Arrays.asList(
R.drawable.a, R.drawable.b, R.drawable.c,
R.drawable.d, R.drawable.e, R.drawable.f,
R.drawable.g, R.drawable.h, R.drawable.i));
}
/**
* Initialize the display item data
*/
public void initDatas() {
LayoutInflater inflater = LayoutInflater.from(mContext);
int count = mDatas.size();
mContainer = (LinearLayout) getChildAt(0);
for (int i = 0; i < count; i++) {
ImageView convertView = (ImageView) inflater.inflate(R.layout.item_layout, null, false);
convertView.setImageResource(mDatas.get(i));
mContainer.addView(convertView);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
mChildWidth = mContainer.getChildAt(0).getMeasuredWidth();
mScrollViewCenter = (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft();
//Find which item is most closed to the screen center
int itemCountOnScreen = Math.round((float)getWidth() / mChildWidth);
int centerToScroll = 0;
int centerDistance = Integer.MAX_VALUE;
for (int j = 0; j < itemCountOnScreen; j++) {
View child = mContainer.getChildAt(j);
int childCenter = getCenterOfChildView(child);
int delta = Math.abs(childCenter - mScrollViewCenter);
if (delta < centerDistance) {
centerDistance = delta;
centerToScroll = childCenter - mScrollViewCenter;
mInitialDisplayedItemIndex = j;
}
}
//Initial the location of the HorizontalScrollView
this.scrollTo(centerToScroll, 0);
mInitialScrollX = centerToScroll > 0 ? centerToScroll : 0;
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
//Force to move to the center of an item.
boundToMoveToCenteredItem();
return true;
}
return super.onTouchEvent(ev);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
mIsScrollRight = l > oldl ? true : false;
startTransform();
}
/**
* Transform the child view
*/
private void startTransform() {
int count = mContainer.getChildCount();
for (int i = 0; i < count; i++) {
View child = mContainer.getChildAt(i);
int childCenter = getCenterOfChildView(child);
int delta = Math.abs(childCenter - mScrollViewCenter);
float scaleFactor = 1.0f;
if (delta > mChildWidth) {
scaleFactor = 0.3f;
} else {
scaleFactor = 0.3f + 0.7f * ((float)(mChildWidth - delta)/mChildWidth);
}
/**
* 此处必须使用属性动画Property-Animation,视图动画View-Animation无法实现连续滑动的3D效果,会出现闪屏!
*
* 1. View-Animation(ScaleAnimation, TranslateAnimation, RotateAnimation):
* 改变的是View的绘制效果,View的属性没有改变,其位置与大小都不变。
* 2. Property-Animation(ValueAnimator, ObjectAnimator)
* 改变是View的属性,View的属性变化的时候,属性动画会自动刷新屏幕,属性动画改变的是对象的真实属性。
*
* Here we use android third party property animation sdk: nineoldandroids-2.4.0.jar
* This library also includes support for animating rotation, translation, alpha,
* and scale on platforms prior to Android 3.0(API-11)
* Reference to:
* http://nineoldandroids.com/
* https://github.com/JakeWharton/NineOldAndroids
*/
ViewHelper.setPivotX(child, child.getWidth() / 2);
ViewHelper.setPivotY(child, child.getHeight() / 2);
ViewHelper.setScaleX(child, scaleFactor);
ViewHelper.setScaleY(child, scaleFactor);
//ViewHelper.setAlpha(child, alpha);
}
}
/**
* Get the center of child view
*/
private int getCenterOfChildView(View view) {
int left = view.getLeft();
int scrollX = this.getScrollX() ;
return left - scrollX + view.getWidth() / 2;
}
/**
* Move to the center of an item according to the direction and distance.
*/
private void boundToMoveToCenteredItem() {
int centerToScroll = 0;
int centerDistance = Integer.MAX_VALUE;
int scrollX = this.getScrollX() - mInitialScrollX;
int current = mInitialDisplayedItemIndex + scrollX / mChildWidth;
int newCenterItemIndex = mInitialDisplayedItemIndex;
//Find which item is most closed to the screen center
for (int i = current - 1; i <= current + 1; i++) {
if (i >= 0 && i < mContainer.getChildCount()) {
View child = mContainer.getChildAt(i);
int childCenter = getCenterOfChildView(child);
int delta = Math.abs(childCenter - mScrollViewCenter);
if (delta < centerDistance) {
centerDistance = delta;
centerToScroll = childCenter - mScrollViewCenter;
newCenterItemIndex = i;
}
}
}
if (centerToScroll < 0) {
//The item that most closed to mScrollViewCenter is located at left of mScrollViewCenter
if (mIsScrollRight) {
if (newCenterItemIndex + 1 < mContainer.getChildCount() - 1) {
//Move to the next item(index = newCenterItemIndex + 1)
this.smoothScrollBy(mChildWidth + centerToScroll, 0);
} else {
//Move to the next item(index = newCenterItemIndex)
this.smoothScrollBy(centerToScroll, 0);
}
} else {
//Move to the next item(index = newCenterItemIndex)
this.smoothScrollBy(centerToScroll, 0);
}
} else {
//The item that most closed to mScrollViewCenter is located at right of mScrollViewCenter
if (mIsScrollRight) {
//Move to the next item(index = newCenterItemIndex)
this.smoothScrollBy(centerToScroll, 0);
} else {
if (newCenterItemIndex - 1 > 0) {
//Move to the previous item(index = newCenterItemIndex - 1)
this.smoothScrollBy(centerToScroll - mChildWidth, 0);
} else {
//Move to the next item(index = newCenterItemIndex)
this.smoothScrollBy(centerToScroll, 0);
}
}
}
}
}
效果图: