Android列表/网格视图标题上的视差效果不会很好地调整大小

时间:2013-12-23 09:18:15

标签: android mono parallax

我正在使用@Vikram hack发现here庄严地模仿标题上的视差效果,就像新的Airbnb和Spotify一样。 代码在我的测试应用程序上工作正常,但是在主应用程序中,以编程方式设置图像大小,效果似乎几乎消失,即图像几乎不会改变位置/滚动。

parallax_activity.xml:

<?xml version="1.0" encoding="utf-8"?>
<cheesebaron.parallaxscrollview.ParallaxScrollView
  xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_canvas_parallax_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  <FrameLayout
      android:id="@+id/parallax_headline"
      android:layout_width="match_parent"
      android:layout_height="300dp"
      android:background="#ff00aa"
      android:scaleType="fitXY" />
  <cheesebaron.parallaxscrollview.ObservableLinearLayout
      android:id="@+id/observable_list"
      android:layout_width="match_parent"
      android:layout_height="match_parent">
    <LinearLayout
        android:id="@+id/list_content_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="300dp"
        android:orientation="vertical">
      <LinearLayout
          android:id="@+id/parallax_list_holder"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"/>
    </LinearLayout>
  </cheesebaron.parallaxscrollview.ObservableLinearLayout>
</cheesebaron.parallaxscrollview.ParallaxScrollView>

ObservableLinearLayout / AnotherView:

public class ObservableLinearLayout : LinearLayout
{
    protected ObservableLinearLayout(IntPtr javaReference, JniHandleOwnership transfer)
        : base(javaReference, transfer)
    {
    }

    public ObservableLinearLayout(Context context)
        : base(context)
    {
    }

    public ObservableLinearLayout(Context context, IAttributeSet attrs)
        : base(context, attrs)
    {
    }

    private IScrollCallbacks mCallbacks;

    public interface IScrollCallbacks
    {
        void OnScrollChanged(int l, int t, int oldl, int oldt);
    }

    public void SetCallbacks(IScrollCallbacks listener)
    {
        mCallbacks = listener;
    }


    //@Override
    public override void Draw(Canvas canvas)
    {
        base.Draw(canvas);
    } 

    //@Override
    protected override void OnScrollChanged(int l, int t, int oldl, int oldt)
    {
        base.OnScrollChanged(l, t, oldl, oldt);
        if (mCallbacks != null)
        {
            mCallbacks.OnScrollChanged(l, t, oldl, oldt);
        }
    }

    //@override
    protected override int ComputeVerticalScrollRange()
    {
        return base.ComputeVerticalScrollRange();
    }
}

ParallaxScrollView.cs:

public class ParallaxScrollView : ViewGroup
{
    private const GravityFlags DefaultChildGravity = GravityFlags.CenterHorizontal;
    private new const string Tag = "ParallaxScrollView";
    //private const float DefaultParallaxOffset = 0.2f;
    private const float DefaultParallaxOffset = 0.5f;

    private float _parallaxOffset = DefaultParallaxOffset;
    private View _background;
    private ObservableScrollView _scrollView;

    private int _backgroundRight;
    private int _backgroundBottom;

    private int _scrollContentHeight;
    private int _scrollViewHeight;

    private float _scrollDiff;

    public float ParallaxOffset
    {
        get { return _parallaxOffset; }
        set
        {
            var offset = (float)Math.Round(value * 100) / 100;
            _parallaxOffset = offset > 0.0 ? offset : DefaultParallaxOffset;

            RequestLayout();
        }
    }

    public ParallaxScrollView(Context context)
        : this(context, null)
    { }

    public ParallaxScrollView(Context context, IAttributeSet attrs)
        : this(context, attrs, 0)
    { }

    public ParallaxScrollView(Context context, IAttributeSet attrs, int defStyle)
        : base(context, attrs, defStyle)
    {
        var a = Context.Theme.ObtainStyledAttributes(attrs, Resource.Styleable.parallaxscrollview, 0, 0);

        try
        {
            ParallaxOffset = a.GetFloat(Resource.Styleable.parallaxscrollview_parallaxOffset,
                                        DefaultParallaxOffset);
        }
        finally
        {
            a.Recycle();
        }

    }

    public override void AddView(View child)
    {
        if (ChildCount > 1)
            throw new ArgumentException("ParallaxScrollView can only host two direct children");
        base.AddView(child);
    }

    public override void AddView(View child, int index)
    {
        if (ChildCount > 1)
            throw new ArgumentException("ParallaxScrollView can only host two direct children");
        base.AddView(child, index);
    }

    public override void AddView(View child, int index, LayoutParams @params)
    {
        if (ChildCount > 1)
            throw new ArgumentException("ParallaxScrollView can only host two direct children");
        base.AddView(child, index, @params);
    }

    public override void AddView(View child, int width, int height)
    {
        if (ChildCount > 1)
            throw new ArgumentException("ParallaxScrollView can only host two direct children");
        base.AddView(child, width, height);
    }

    public override void AddView(View child, LayoutParams @params)
    {
        if (ChildCount > 1)
            throw new ArgumentException("ParallaxScrollView can only host two direct children");
        base.AddView(child, @params);
    }

    protected override void OnFinishInflate()
    {
        base.OnFinishInflate();

        if (ChildCount > 2)
            throw new InvalidOperationException("ParallaxScrollView can only host two direct children");

        OrganiseViews();
    }

    protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        base.OnMeasure(widthMeasureSpec, heightMeasureSpec);

        if (_scrollView != null)
        {
            MeasureChild(_scrollView, MeasureSpec.MakeMeasureSpec(
                MeasureSpec.GetSize(widthMeasureSpec), MeasureSpecMode.AtMost),
                MeasureSpec.MakeMeasureSpec(MeasureSpec.GetSize(heightMeasureSpec),
                MeasureSpecMode.AtMost));

            _scrollContentHeight = _scrollView.GetChildAt(0).MeasuredHeight;
            _scrollViewHeight = _scrollView.MeasuredHeight;
        }
        if (_background != null)
        {
            var minHeight = (int)(_scrollViewHeight + _parallaxOffset
                               * (_scrollContentHeight - _scrollViewHeight));
            minHeight = Math.Max(minHeight, MeasureSpec.GetSize(heightMeasureSpec));

            MeasureChild(_background, MeasureSpec.MakeMeasureSpec(
                MeasureSpec.GetSize(widthMeasureSpec), MeasureSpecMode.Exactly),
                MeasureSpec.MakeMeasureSpec(minHeight, MeasureSpecMode.Exactly));

            _backgroundRight = Left + _background.MeasuredWidth;
            _backgroundBottom = Top + _background.MeasuredHeight;

            _scrollDiff = (_background.MeasuredHeight - _scrollViewHeight)
                          / (float)(_scrollContentHeight - _scrollViewHeight);
        }
    }

    protected override void OnLayout(bool changed, int l, int t, int r, int b)
    {
        var parentLeft = PaddingLeft;
        var parentRight = r - l - PaddingRight;
        var parentTop = PaddingTop;
        var parentBottom = b - t - PaddingBottom;

        if (_scrollView != null && _scrollView.Visibility != ViewStates.Gone)
        {
            var lp = (FrameLayout.LayoutParams)_scrollView.LayoutParameters;
            var width = _scrollView.MeasuredWidth;
            var height = _scrollView.MeasuredHeight;

            int childLeft;
            int childTop;

            var gravity = lp.Gravity;
            if (gravity == GravityFlags.NoGravity)
                gravity = DefaultChildGravity;

            var horizontalGravity = gravity & GravityFlags.HorizontalGravityMask;
            var verticalGravity = gravity & GravityFlags.VerticalGravityMask;

            switch (horizontalGravity)
            {
                case GravityFlags.Left:
                    childLeft = parentLeft - lp.LeftMargin;
                    break;
                case GravityFlags.CenterHorizontal:
                    childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + lp.LeftMargin - lp.RightMargin;
                    break;
                case GravityFlags.Right:
                    childLeft = parentRight - width - lp.RightMargin;
                    break;
                default:
                    childLeft = parentLeft + lp.LeftMargin;
                    break;
            }

            switch (verticalGravity)
            {
                case GravityFlags.Top:
                    childTop = parentTop + lp.TopMargin;
                    break;
                case GravityFlags.CenterVertical:
                    childTop = parentTop + (parentBottom - parentTop - height) / 2 + lp.TopMargin - lp.BottomMargin;
                    break;
                case GravityFlags.Bottom:
                    childTop = parentBottom - height - lp.BottomMargin;
                    break;
                default:
                    childTop = parentTop + lp.TopMargin;
                    break;
            }

            _scrollView.Layout(childLeft, childTop, childLeft + width, childTop + height);
        }

        if (_background != null && _scrollView != null)
        {
            //var scrollYCenterOffset = -_scrollView.ScrollY;
            var scrollYCenterOffset = _scrollView.ScrollY;
            //changes the velocity of the effect
            var offset = (int)((scrollYCenterOffset * _scrollDiff) / 2);
            _background.Layout(Left, offset, _backgroundRight, offset + _backgroundBottom);
        }
    }

    public override LayoutParams GenerateLayoutParams(IAttributeSet attrs)
    {
        return new FrameLayout.LayoutParams(Context, attrs);
    }

    protected override LayoutParams GenerateLayoutParams(LayoutParams p)
    {
        return new FrameLayout.LayoutParams(p);
    }

    protected override LayoutParams GenerateDefaultLayoutParams()
    {
        return new FrameLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent,
                                            GravityFlags.CenterHorizontal);
    }

    protected override bool CheckLayoutParams(LayoutParams p)
    {
        return p is FrameLayout.LayoutParams;
    }

    private void OrganiseViews()
    {
        if (ChildCount <= 0) return;

        if (ChildCount == 1)
        {
            var foreground = GetChildAt(0);
            OrganiseBackgroundView(null);
            OrganiseForegroundView(foreground);
        }
        else if (ChildCount == 2)
        {
            var background = GetChildAt(0);
            var foreground = GetChildAt(1);

            OrganiseBackgroundView(background);
            OrganiseForegroundView(foreground);
        }
        else
            throw new InvalidOperationException("ParallaxScrollView can host only two direct children");
    }

    private void OrganiseBackgroundView(View background)
    {
        _background = background;
    }

    private void OrganiseForegroundView(View foreground)
    {
        var insertPos = ChildCount - 1;

        if (foreground is ObservableScrollView)
        {
            _scrollView = (ObservableScrollView)foreground;
            _scrollView.ScrollChanged -= ScrollViewOnScrollChanged;
        }
        else if (foreground is ViewGroup && !(foreground is ScrollView))
        {
            _scrollView = new ObservableScrollView(Context, null);
            RemoveView(foreground);
            _scrollView.AddView(foreground);
            AddView(_scrollView, insertPos);
        }
        else if (foreground is ScrollView)
        {
            var child = ((ScrollView)foreground).ChildCount > 0 ?
                ((ScrollView)foreground).GetChildAt(0) : null;

            _scrollView = new ObservableScrollView(Context, null);
            RemoveView(foreground);
            if (child != null)
                _scrollView.AddView(child);
            AddView(_scrollView, insertPos);
        }
        else
        {
            _scrollView = new ObservableScrollView(Context, null);
            RemoveView(foreground);
            _scrollView.AddView(foreground);
            AddView(_scrollView, insertPos);
        }

        if (_scrollView != null)
        {
            _scrollView.LayoutParameters = foreground.LayoutParameters;
            _scrollView.ScrollChanged += ScrollViewOnScrollChanged;
            _scrollView.FillViewport = true;
        }
    }

    private void ScrollViewOnScrollChanged(object sender, ObservableScrollViewEventArgs args)
    {
        RequestLayout();
    }
}
}

调用函数:

private void BuildMobileListView()
{
    var listContainer = FindViewById<LinearLayout>(Resource.Id.parallax_list_holder);
    var listMainHolder = FindViewById<LinearLayout>(Resource.Id.list_content_container);
    var listView = new MvxListView(listContainer.Context, null, new MainCanvasListAdapter(this, (IMvxAndroidBindingContext) BindingContext))
    {
        VerticalFadingEdgeEnabled = false,
        //the first item is the headline you get it separately
        ItemsSource = ViewModel.Items.GetRange(1, ViewModel.Items.Count-1),
        LayoutParameters = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.FillParent),
        CacheColorHint = Color.Transparent,
    };

    listView.VerticalFadingEdgeEnabled = false;
    listView.Divider = null;
    listContainer.AddView(listView);
    listView.Post(() =>
    {
        SetScrollableViewHeight(listView, listContainer);
        var p = (LinearLayout.LayoutParams)listMainHolder.LayoutParameters;
        listMainHolder.LayoutParameters = p;
    });
}

帖子功能:

public void SetScrollableViewHeight(GridView gridView, LinearLayout llMain)
        {
            var gridViewAdapter = gridView.Adapter;
            if (gridViewAdapter == null)
            {
                return;
            }

            var totalHeight = 0;
            var firstHeight = 0;
            var desiredWidth = View.MeasureSpec.MakeMeasureSpec(
                                 gridView.Width, MeasureSpecMode.AtMost);

            for (int i = 0; i < gridViewAdapter.Count; i++)
            {

                if (i == 0)
                {
                    View listItem = gridViewAdapter.GetView(i, null, gridView);
                    listItem.Measure(desiredWidth, (int)MeasureSpecMode.Unspecified);
                    firstHeight = listItem.MeasuredHeight + DroidAttributes.MOBILLE_GRID_VIEW_MARGIN*2;
                }
                totalHeight += firstHeight;
            }
            totalHeight = totalHeight / 2;

            var lParams = (LinearLayout.LayoutParams)llMain.LayoutParameters;

            lParams.Height = totalHeight /*+ DroidAttributes.MOBILLE_GRID_VIEW_MARGIN * 2*//*+ (gridView.ListPaddingBottom * (gridViewAdapter.Count - 1)/2)*/;
            llMain.LayoutParameters = lParams;
            _mobileLayoutContainer.RequestLayout();
        }

@ + id / list_content_container上设置的边距是根据标题图片的宽度/高度以编程方式设置的。

我一直在不同的项目上使用几乎相同的代码并获得更好的“视差”结果,有人能指出我的内容

0 个答案:

没有答案