我正在使用@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上设置的边距是根据标题图片的宽度/高度以编程方式设置的。
我一直在不同的项目上使用几乎相同的代码并获得更好的“视差”结果,有人能指出我的内容