parallax(視差)の効果のソースコード分析

16689 ワード

転載は出所を明記してください.
http://blog.csdn.net/coder_nice/articale/detail/45311715
視差概念
多分多くの人はparallaxの概念を知らないかもしれません.まず動画を見に来てください.
この図があるならば、パララックスの視差の概念が分かります.google palyはこの効果があります.多くの人が効果図を見たら、すぐにリンクを得て、いい人が最後までやりたいと思います.githubアドレス
最初の考え
最初にデザイナーからこの効果を要求された時、自分でよく考えました.一つのFraam Layoutの中に二層のviewが含まれています.下の階はスライドに従って視差の効果が現れるparallax viewです.上の階は普通のviewで、ジェスチャーのスライドに従ってスライドします.上の方のスライド速度を下の方のパラレークスビューに伝えて、速度値を小さくしてください.これも多くの人の考えかもしれません.githubに接触したこのオープンソースプロジェクトの後、コードを見てみましたが、これよりはるかに簡単です.
ソース分析
ここでは最もよく使われているParalaxScrrollViewの視差効果のあるScrrollViewの解析だけをして、まずコードを見ます.ParalaxScrrollViewソース
public class ParallaxScrollView extends ScrollView {

    private static final int DEFAULT_PARALLAX_VIEWS = 1;
    private static final float DEFAULT_INNER_PARALLAX_FACTOR = 1.9F;
    private static final float DEFAULT_PARALLAX_FACTOR = 1.9F;
    private static final float DEFAULT_ALPHA_FACTOR = -1F;
    private int numOfParallaxViews = DEFAULT_PARALLAX_VIEWS;
    private float innerParallaxFactor = DEFAULT_PARALLAX_FACTOR;
    private float parallaxFactor = DEFAULT_PARALLAX_FACTOR;
    private float alphaFactor = DEFAULT_ALPHA_FACTOR;
    private ArrayList<ParallaxedView> parallaxedViews = new ArrayList<ParallaxedView>();

    public ParallaxScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    public ParallaxScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public ParallaxScrollView(Context context) {
        super(context);
    }

    protected void init(Context context, AttributeSet attrs) {
        TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.ParallaxScroll);
        this.parallaxFactor = typeArray.getFloat(R.styleable.ParallaxScroll_parallax_factor, DEFAULT_PARALLAX_FACTOR);
        this.alphaFactor = typeArray.getFloat(R.styleable.ParallaxScroll_alpha_factor, DEFAULT_ALPHA_FACTOR);
        this.innerParallaxFactor = typeArray.getFloat(R.styleable.ParallaxScroll_inner_parallax_factor, DEFAULT_INNER_PARALLAX_FACTOR);
        this.numOfParallaxViews = typeArray.getInt(R.styleable.ParallaxScroll_parallax_views_num, DEFAULT_PARALLAX_VIEWS);
        typeArray.recycle();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        makeViewsParallax();
    }

    private void makeViewsParallax() {
        if (getChildCount() > 0 && getChildAt(0) instanceof ViewGroup) {
            ViewGroup viewsHolder = (ViewGroup) getChildAt(0);
            int numOfParallaxViews = Math.min(this.numOfParallaxViews, viewsHolder.getChildCount());
            for (int i = 0; i < numOfParallaxViews; i++) {
                ParallaxedView parallaxedView = new ScrollViewParallaxedItem(viewsHolder.getChildAt(i));
                parallaxedViews.add(parallaxedView);
            }
        }
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        float parallax = parallaxFactor;
        float alpha = alphaFactor;
        for (ParallaxedView parallaxedView : parallaxedViews) {
            parallaxedView.setOffset((float)t / parallax);
            parallax *= innerParallaxFactor;
            if (alpha != DEFAULT_ALPHA_FACTOR) {
                float fixedAlpha = (t <= 0) ? 1 : (100 / ((float)t * alpha));
                parallaxedView.setAlpha(fixedAlpha);
                alpha /= alphaFactor;
            }
            parallaxedView.animateNow();
        }
    }

    protected class ScrollViewParallaxedItem extends ParallaxedView {

        public ScrollViewParallaxedItem(View view) {
            super(view);
        }

        @Override
        protected void translatePreICS(View view, float offset) {
            view.offsetTopAndBottom((int)offset - lastOffset);
            lastOffset = (int)offset;
        }
    }
}
ParalaxedViewソース
public abstract class ParallaxedView {
    static public boolean isAPI11 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
    protected WeakReference<View> view;
    protected int lastOffset;
    protected List<Animation> animations;

    abstract protected void translatePreICS(View view, float offset);

    public ParallaxedView(View view) {
        this.lastOffset = 0;
        this.animations = new ArrayList<Animation>();
        this.view = new WeakReference<View>(view);
    }

    public boolean is(View v) {
        return (v != null && view != null && view.get() != null && view.get().equals(v));
    }

    @SuppressLint("NewApi")
    public void setOffset(float offset) {
        View view = this.view.get();
        if (view != null)
            if (isAPI11) {
                view.setTranslationY(offset);
            } else {
                translatePreICS(view, offset);
            }
    }

    public void setAlpha(float alpha) {
        View view = this.view.get();
        if (view != null)
            if (isAPI11) {
                view.setAlpha(alpha);
            } else {
                alphaPreICS(view, alpha);
            }
    }

    protected synchronized void addAnimation(Animation animation) {
        animations.add(animation);
    }

    protected void alphaPreICS(View view, float alpha) {
        addAnimation(new AlphaAnimation(alpha, alpha));
    }

    protected synchronized void animateNow() {
        View view = this.view.get();
        if (view != null) {
            AnimationSet set = new AnimationSet(true);
            for (Animation animation : animations)
                if (animation != null)
                    set.addAnimation(animation);
            set.setDuration(0);
            set.setFillAfter(true);
            view.setAnimation(set);
            set.start();
            animations.clear();
        }
    }

    public void setView(View view) {
        this.view = new WeakReference<View>(view);
    }
大体の流れ
ParaxScrrollViewのソースコードはまだ簡単です.ほとんどのコードは説明せずにわかると思います.大体の流れはParalaxScrrollViewを作成した後、TypedArayのいくつかのパラメータ(最初の速度減衰率、色グラデーション率、視差の速度減衰率、視差効果view個数)をロードして、視差効果のあるviewを必要とします.(numOfParallaxViewパラメータを用いてget ChildAt法により取得した)パラメータとしてScrrollView ParalexedItemを作成し(視差効果のある方法のviewは、単にカプセル化されているだけ)、その後はparallaxedviewsリストに追加して、オンScrrollChend()方法の中で循環するparallaxedviewsリストは、各視差が必要なviewをこのジェスチャーでスライドする視差効果を設定してOKです.
重点的にonScrrollChangedの方法を話します.
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        float parallax = parallaxFactor;
        float alpha = alphaFactor;
        for (ParallaxedView parallaxedView : parallaxedViews) {
            parallaxedView.setOffset((float)t / parallax);
            parallax *= innerParallaxFactor;
            if (alpha != DEFAULT_ALPHA_FACTOR) {
                float fixedAlpha = (t <= 0) ? 1 : (100 / ((float)t * alpha));
                parallaxedView.setAlpha(fixedAlpha);
                alpha /= alphaFactor;
            }
            parallaxedView.animateNow();
        }
    }
forループは、複数の視差viewがあるときに使用されます.alphaはグラデーションで使用され、視差の役割を果たすのはこの言葉です.
parallaxedView.setOffset((float)t / parallax);
この言葉の方法体の中で何をしていますか?
    @SuppressLint("NewApi")
    public void setOffset(float offset) {
        View view = this.view.get();
        if (view != null)
            if (isAPI11) {
                view.setTranslationY(offset);
            } else {
                translatePreICS(view, offset);
            }
    }
APIが11より大きい時に使うのはこの方法view.set Translation Yで、11より小さい時に使うview.offset TopAndBottom(int)offset-lastOffsetです.この方法は高いバージョンのview.set Translation(offset)方法だけを見ます.
APIの説明を見てください
APIを見て、viewのこの方法をどう説明しますか?
Sets the vertical location of this view relative to its top position. This effectively positions the object post-layout, in addition to wherever the object's layout placed it.

Related XML Attributes
android:translationY
Parameters
translationY    The vertical position of this view relative to its top position, in pixels.
縦方向のview上部に対する位置を設定します.この位置はオブジェクトがlayout()メソッドを呼び出した後に有効になります.このオブジェクトのlayout()メソッドの中にそれを再配置した場合だけ有効になりません.
作者(コードの持ち主)の考えを理解してください.
著者は、ユーザがスクリーンをスライドさせるたびに、ScrelViewのオンスクリーン()に視差view距離の上部の位置を設定したいです.このように視差viewの滑り速度が遅く、通常のviewの滑りが速い効果が現れました.伝達速度のような数値は全く必要ないです.ただ簡単に頂上までの位置を設定して、問題を簡略化しました.
締め括りをつける
私は比較的簡単なParalaxScrrollViewのソースコードだけを話しましたが、他のいくつかのやや複雑な視差ViewのParalaxListView、ParaxExpandable ListViewも大同小異です.問題を理解するのはもちろん簡単に始めるべきです.