为了账号安全,请及时绑定邮箱和手机立即绑定

拥有三个类轻轻松松实现3D画廊(一)

标签:
Android

首先我们拿到这三个类:分别是FancyCoverFlow、FancyCoverFlowAdapter、FancyCoverFlowItemWrapper。这三个类分别有不同的含义。
FancyCoverFlow:这个类是控件的主类,要在xml(布局文件)里面写的。

FancyCoverFlowAdapter:这个类是获取图片的资源(当然这个不一定是图片),获取资源的那个类必须要继承FancyCoverFlowAdapter类。里面会有4个方法有点像ListView里面的Item一样。getCount();getItem;getItemId();还有getCoverFlowItem();我要提的一点是里面的布局文件要自己纯代码来写,我试过用View vie = mInflater.inflate(R.layout.xxx,null);来获取里面的布局,这样写的话,无法得到我们想要的结果。我们只能通过纯代码来创建布局。

FancyCoverFlowItemWrapper:这个类的话是设置每个Item的倒影。

好了,如果其他的类都弄好了,接下来就只有调用几个方法就OK了。

比如我在一个MainActivity创建的一个布局文件。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
tools:context="com.example.office.dierchi.MainActivity">
<com.example.office.dierchi.FancyCoverFlow
android:id="@+id/fancycover"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.example.office.dierchi.FancyCoverFlow>
</LinearLayout>
结下来我们只要在MainActivity中简单的设置就OK,我们可以设置未选中的图片的饱和度、未被选中的图片的比例、图片之间的距离、设置图片是否要倒影等等。(这里面要值得注意的是,每个图片的来源都在继承FancyCoverFlowAdapter类里面设置的,还有每张图片的显示的大小也在那里设置imageView.setLayoutParams(new FancyCoverFlow.LayoutParams(500, 680));)

package com.example.office.dierchi;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {
private FancyCoverFlow fancyCoverFlow;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fancyCoverFlow= (FancyCoverFlow) findViewById(R.id.fancycover);
//设置数据的来源
fancyCoverFlow.setAdapter(new FancyCoverFloeShap());
//设置透明度 未被选中的的透明度
fancyCoverFlow.setUnselectedAlpha(0.3f);
//设置未被选中的饱和度
fancyCoverFlow.setUnselectedSaturation(0.4f);
//设置未被选中的缩放的比例
fancyCoverFlow.setUnselectedScale(0.2f);
//设置图片的距离
fancyCoverFlow.setSpacing(1);
//设置未被选中图片的旋转的角度
fancyCoverFlow.setScaleDownGravity(0.3f);
//3D的倒影
fancyCoverFlow.setReflectionEnabled(true);
fancyCoverFlow.setActionDistance(FancyCoverFlow.ACTION_DISTANCE_AUTO);
}
public class FancyCoverFloeShap extends FancyCoverFlowAdapter{
//设置图片的来源
private int [] images={R.mipmap.image1,R.mipmap.image2,R.mipmap.image3,R.mipmap.image4,R.mipmap.image5,R.mipmap.image6};
@Override
public int getCount() {
return images.length;
}
@Override
public Integer getItem(int position) {
return images[position];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getCoverFlowItem(int i, View reusableView, ViewGroup parent) {
ImageView imageView = null;
//有可以重用的ImageView对象。直接返回即可
if (reusableView != null) {
imageView = (ImageView) reusableView;
} else {
//如果为空,没有新的ImageView,就创建新的View控件
imageView = new ImageView(parent.getContext());
//设置显示类型,缩放类型 CENTER_INSIDE:在中心内部显示
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
//设置显示的尺寸
imageView.setLayoutParams(new FancyCoverFlow.LayoutParams(500, 680));
}
//设置图像资源
imageView.setImageResource(this.getItem(i));
return imageView;
}
}
}

好的,现在我把其他的几个类的代码都复制上去。
首先是FancyCoverFlow类
/*

  • Copyright 2015 David Schreiber
  • 2015 John Paul Nalog
  • Licensed under the Apache License, Version 2.0 (the "License");
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  • http://www.apache.org/licenses/LICENSE-2.0
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an "AS IS" BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

package cn.wom.fancycoverflow;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.*;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Transformation;
import android.widget.Gallery;
import android.widget.SpinnerAdapter;
/**

  • 3D画廊的核心实现类
  • @author WUPENG
  • */
    @SuppressWarnings("deprecation")
    public class FancyCoverFlow extends Gallery {

    // =============================================================================
    // Constants
    // =============================================================================

    public static final int ACTION_DISTANCE_AUTO = Integer.MAX_VALUE;

    public static final float SCALEDOWN_GRAVITY_TOP = 0.0f;

    public static final float SCALEDOWN_GRAVITY_CENTER = 0.5f;

    public static final float SCALEDOWN_GRAVITY_BOTTOM = 1.0f;

    // =============================================================================
    // Private members
    // =============================================================================

    private float reflectionRatio = 0.4f;

    private int reflectionGap = 20;

    private boolean reflectionEnabled = false;

    /**

    • TODO: Doc
      */
      private float unselectedAlpha;

    /**

    • Camera used for view transformation.
      */
      private Camera transformationCamera;

    /**

    • TODO: Doc
      */
      private int maxRotation = 75;

    /**

    • Factor (0-1) that defines how much the unselected children should be scaled down. 1 means no scaledown.
      */
      private float unselectedScale;

    /**

    • TODO: Doc
      */
      private float scaleDownGravity = SCALEDOWN_GRAVITY_CENTER;

    /**

    • Distance in pixels between the transformation effects (alpha, rotation, zoom) are applied.
      */
      private int actionDistance;

    /**

    • Saturation factor (0-1) of items that reach the outer effects distance.
      */
      private float unselectedSaturation;

    // =============================================================================
    // Constructors
    // =============================================================================

    public FancyCoverFlow(Context context) {
    super(context);
    this.initialize();
    }

    public FancyCoverFlow(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.initialize();
    this.applyXmlAttributes(attrs);
    }

    public FancyCoverFlow(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    this.initialize();
    this.applyXmlAttributes(attrs);
    }

    private void initialize() {
    this.transformationCamera = new Camera();
    this.setSpacing(0);
    }

    private void applyXmlAttributes(AttributeSet attrs) {
    TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.FancyCoverFlow);

    this.actionDistance = a.getInteger(R.styleable.FancyCoverFlow_actionDistance, ACTION_DISTANCE_AUTO);
    this.scaleDownGravity = a.getFloat(R.styleable.FancyCoverFlow_scaleDownGravity, 1.0f);
    this.maxRotation = a.getInteger(R.styleable.FancyCoverFlow_maxRotation, 45);
    this.unselectedAlpha = a.getFloat(R.styleable.FancyCoverFlow_unselectedAlpha, 0.3f);
    this.unselectedSaturation = a.getFloat(R.styleable.FancyCoverFlow_unselectedSaturation, 0.0f);
    this.unselectedScale = a.getFloat(R.styleable.FancyCoverFlow_unselectedScale, 0.75f);

    }

    // =============================================================================
    // Getter / Setter
    // =============================================================================

    public float getReflectionRatio() {
    return reflectionRatio;
    }

    public void setReflectionRatio(float reflectionRatio) {
    if (reflectionRatio <= 0 reflectionRatio > 0.5f) {
    throw new IllegalArgumentException("reflectionRatio may only be in the interval (0, 0.5]");
    }

    this.reflectionRatio = reflectionRatio;
    
    if (this.getAdapter() != null) {
        ((FancyCoverFlowAdapter) this.getAdapter()).notifyDataSetChanged();
    }

    }

    public int getReflectionGap() {
    return reflectionGap;
    }

    public void setReflectionGap(int reflectionGap) {
    this.reflectionGap = reflectionGap;

    if (this.getAdapter() != null) {
        ((FancyCoverFlowAdapter) this.getAdapter()).notifyDataSetChanged();
    }

    }

    public boolean isReflectionEnabled() {
    return reflectionEnabled;
    }

    public void setReflectionEnabled(boolean reflectionEnabled) {
    this.reflectionEnabled = reflectionEnabled;

    if (this.getAdapter() != null) {
        ((FancyCoverFlowAdapter) this.getAdapter()).notifyDataSetChanged();
    }

    }

    /**

    • Use this to provide a {@link FancyCoverFlowAdapter} to the coverflow. This
    • method will throw an {@link ClassCastException} if the passed adapter does not
    • subclass {@link FancyCoverFlowAdapter}.
    • @param adapter
    • 在类中重写类父类的setAdapter方法
    • 要求Adapter必须是实现的FancyCoverFlowAdapter的对象或其子类的对象,
    • 如果不是这个对象就会抛出异常
      */
      @Override
      public void setAdapter(SpinnerAdapter adapter) {
      if (!(adapter instanceof FancyCoverFlowAdapter)) {
      throw new ClassCastException(FancyCoverFlow.class.getSimpleName() + " only works in conjunction with a " + FancyCoverFlowAdapter.class.getSimpleName());
      }

      super.setAdapter(adapter);
      }

    /**

    • Returns the maximum rotation that is applied to items left and right of the center of the coverflow.
    • @return
      */
      public int getMaxRotation() {
      return maxRotation;
      }

    /**

    • Sets the maximum rotation that is applied to items left and right of the center of the coverflow.
    • @param maxRotation
      */
      public void setMaxRotation(int maxRotation) {
      this.maxRotation = maxRotation;
      }

    /**

    • TODO: Write doc
    • @return
      */
      public float getUnselectedAlpha() {
      return this.unselectedAlpha;
      }

    /**

    • TODO: Write doc
    • @return
      */
      public float getUnselectedScale() {
      return unselectedScale;
      }

    /**

    • TODO: Write doc
    • @param unselectedScale
      */
      public void setUnselectedScale(float unselectedScale) {
      this.unselectedScale = unselectedScale;
      }

    /**

    • TODO: Doc
    • @return
      */
      public float getScaleDownGravity() {
      return scaleDownGravity;
      }

    /**

    • TODO: Doc
    • @param scaleDownGravity
      */
      public void setScaleDownGravity(float scaleDownGravity) {
      this.scaleDownGravity = scaleDownGravity;
      }

    /**

    • TODO: Write doc
    • @return
      */
      public int getActionDistance() {
      return actionDistance;
      }

    /**

    • TODO: Write doc
    • @param actionDistance
      */
      public void setActionDistance(int actionDistance) {
      this.actionDistance = actionDistance;
      }

    /**

    • TODO: Write doc
    • @param unselectedAlpha
      */
      @Override
      public void setUnselectedAlpha(float unselectedAlpha) {
      super.setUnselectedAlpha(unselectedAlpha);
      this.unselectedAlpha = unselectedAlpha;
      }

    /**

    • TODO: Write doc
    • @return
      */
      public float getUnselectedSaturation() {
      return unselectedSaturation;
      }

    /**

    • TODO: Write doc
    • @param unselectedSaturation
      */
      public void setUnselectedSaturation(float unselectedSaturation) {
      this.unselectedSaturation = unselectedSaturation;
      }

    /**

    • 作用:当Gallery绘制每一个Item时会调用该方法来产生一些动画的特效(实现原理:内部是一个矩阵的转换)
    • 该方法中包括:
    • 1:透明度
    • 2:饱和度
    • 3:旋转
    • 4:缩放
      */
      @Override
      protected boolean getChildStaticTransformation(View child, Transformation t) {
      // 获取当前Item对象
      FancyCoverFlowItemWrapper item = (FancyCoverFlowItemWrapper) child;

      // 如果版本大于等于16时 刷新页面 Android本身原因
      if (android.os.Build.VERSION.SDK_INT >= 16) {
      item.invalidate();
      }
      //获取控件的宽度
      final int coverFlowWidth = this.getWidth();
      //获取控件宽度的一半
      final int coverFlowCenter = coverFlowWidth / 2;
      //获取Item的宽度(中心横坐标)
      final int childWidth = item.getWidth();
      //获取Item的高度
      final int childHeight = item.getHeight();
      //获取Item的中心位置
      final int childCenter = item.getLeft() + childWidth / 2;

      /**

      • 每一个Item距离中心的距离
      • 如果等于设置的常量
        */

      final int actionDistance = (this.actionDistance == ACTION_DISTANCE_AUTO) ?
      (int) ((coverFlowWidth + childWidth) / 2.0f) : this.actionDistance;

      // 保证这个变量的值-1.0到1.0之间 item距离中心点越远,该值越大
      final float effectsAmount = Math.min(1.0f, Math.max(-1.0f, (1.0f / actionDistance) * (childCenter - coverFlowCenter)));

      // 清除以前的变换
      t.clear();
      //设置类型,同时进行alpha和matrix变换
      t.setTransformationType(Transformation.TYPE_BOTH);

      // 透明度变换
      if (this.unselectedAlpha != 1) {
      final float alphaAmount = (this.unselectedAlpha - 1) * Math.abs(effectsAmount) + 1;
      t.setAlpha(alphaAmount);
      }

      // 饱和度变换
      if (this.unselectedSaturation != 1) {
      final float saturationAmount = (this.unselectedSaturation - 1) * Math.abs(effectsAmount) + 1;
      item.setSaturation(saturationAmount);
      }

      final Matrix imageMatrix = t.getMatrix();

      // 旋转变化.
      if (this.maxRotation != 0) {
      //计算旋转角度(越远离中心点的item,旋转角度就会越大)
      final int rotationAngle = (int) (-effectsAmount * this.maxRotation);
      this.transformationCamera.save();
      this.transformationCamera.rotateY(rotationAngle);
      this.transformationCamera.getMatrix(imageMatrix);
      this.transformationCamera.restore();
      }

      // 缩放变换.
      if (this.unselectedScale != 1) {
      //远离中心点的item就会变得更小
      final float zoomAmount = (this.unselectedScale - 1) Math.abs(effectsAmount) + 1;
      // 平移的相对水平距离
      final float translateX = childWidth / 2.0f;
      // 平移的相对垂直距离
      final float translateY = childHeight
      this.scaleDownGravity;
      //将锚点移动到坐标原点(左下角)
      imageMatrix.preTranslate(-translateX, -translateY);
      //开始缩放
      imageMatrix.postScale(zoomAmount, zoomAmount);
      //将锚点重新移动到中心点
      imageMatrix.postTranslate(translateX, translateY);
      }

      return true;
      }

    // =============================================================================
    // Public classes
    // =============================================================================

    public static class LayoutParams extends Gallery.LayoutParams {
    public LayoutParams(Context c, AttributeSet attrs) {
    super(c, attrs);
    }

    public LayoutParams(int w, int h) {
        super(w, h);
    }
    
    public LayoutParams(ViewGroup.LayoutParams source) {
        super(source);
    }

    }
    }
    ——————————————————————————————————————————————————
    如果就这样直接复制FancyCoverFlow类的话就会报错,因为在value里面加一个attr.xml文件。里面的文件内容如下:
    < ?xml version="1.0" encoding="utf-8"?>
    < resources>
    < declare-styleable name="FancyCoverFlow">
    < attr name="unselectedAlpha" format="float"/>
    < attr name="unselectedSaturation" format="float"/>
    < attr name="unselectedScale" format="float"/>
    < attr name="maxRotation" format="integer"/>
    < attr name="scaleDownGravity" format="float"/>
    < attr name="actionDistance" format="integer">
    < enum name="auto" value="2147483647" />
    < /attr>
    < /declare-styleable>
    < /resources>
    ——————————————————————————————————————————————————

接下是FancyCoverFlowAdapter类

package cn.wom.fancycoverflow;

import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

/**

  • 3D画廊的核心实现类Adapter
  • @author WUPENG
  • */
    public abstract class FancyCoverFlowAdapter extends BaseAdapter {

    // =============================================================================
    // Supertype overrides
    // =============================================================================

    @Override
    public final View getView(int i, View reusableView, ViewGroup viewGroup) {
    FancyCoverFlow coverFlow = (FancyCoverFlow) viewGroup;

    View wrappedView = null;
    FancyCoverFlowItemWrapper coverFlowItem;
    
    if (reusableView != null) {
        coverFlowItem = (FancyCoverFlowItemWrapper) reusableView;
        wrappedView = coverFlowItem.getChildAt(0);
        coverFlowItem.removeAllViews();
    } else {
        coverFlowItem = new FancyCoverFlowItemWrapper(viewGroup.getContext());
    }
    
    wrappedView = this.getCoverFlowItem(i, wrappedView, viewGroup);
    
    if (wrappedView == null) {
        throw new NullPointerException("getCoverFlowItem() was expected to return a view, but null was returned.");
    }
    //实现3D倒影的效果
    final boolean isReflectionEnabled = coverFlow.isReflectionEnabled();
    coverFlowItem.setReflectionEnabled(isReflectionEnabled);
    
    if(isReflectionEnabled) {
        coverFlowItem.setReflectionGap(coverFlow.getReflectionGap());
        coverFlowItem.setReflectionRatio(coverFlow.getReflectionRatio());
    }
    
    coverFlowItem.addView(wrappedView);
    coverFlowItem.setLayoutParams(wrappedView.getLayoutParams());
    
    return coverFlowItem;

    }

    // =============================================================================
    // Abstract methods
    // =============================================================================

    public abstract View getCoverFlowItem(int position, View reusableView, ViewGroup parent);
    }

点击查看更多内容
4人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
15
获赞与收藏
320

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消