xamarin forms label in a scroll view pinch and zoom android - xamarin.android

I need a label with large text like a article which needs pinch to zoom capability for that I have written a ZoomableScrollview which works fine in IOS and Windows but not in android. Please see the code below.
Code in PCL
public class ZoomableScrollView:ScrollView
{
public static readonly BindableProperty MinimumZoomScaleProperty = BindableProperty.Create("MinimumZoomScale", typeof(float), typeof(ZoomableScrollView), default(float));
public float MinimumZoomScale
{
get { return (float)GetValue(MinimumZoomScaleProperty); }
set { SetValue(MinimumZoomScaleProperty, value); }
}
public static readonly BindableProperty MaximumZoomScaleProperty = BindableProperty.Create("MaximumZoomScale", typeof(float), typeof(ZoomableScrollView), default(float));
public float MaximumZoomScale
{
get { return (float)GetValue(MaximumZoomScaleProperty); }
set { SetValue(MaximumZoomScaleProperty, value); }
}
}
IOS renderer
public class ZoomableScrollViewRenderer : ScrollViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
if (e.OldElement == null)
{
ZoomableScrollView zsv = Element as ZoomableScrollView;
this.MinimumZoomScale = zsv.MinimumZoomScale;
this.MaximumZoomScale = zsv.MaximumZoomScale;
this.ViewForZoomingInScrollView += (UIScrollView sv) => { return this.Subviews[0]; };
}
}
}
Windows renderer
public class ZoomableScrollViewRenderer:ScrollViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<ScrollView> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
return;
if (e.OldElement == null)
{
ZoomableScrollView zsv = Element as ZoomableScrollView;
this.Control.ZoomMode = Windows.UI.Xaml.Controls.ZoomMode.Enabled;
this.Control.MinZoomFactor = zsv.MinimumZoomScale;
this.Control.MaxZoomFactor = zsv.MaximumZoomScale;
}
}
}
Android renderer
public class ZoomableScrollViewRenderer:ScrollViewRenderer
{
float originalDistanceX, currentdistanceX, originalDistanceY, currentdistanceY;
bool IsPinching = false;
double currentScale;
TeluguLabel lbl;
ScrollView svMain, svSub;
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
svMain = ((ScrollView)e.NewElement);
lbl = svMain.Content as TeluguLabel;
svSub = new ScrollView();
svSub.Orientation = ScrollOrientation.Horizontal;
svSub.Content = lbl;
svMain.Content = svSub;
lbl.AnchorX = 0;
lbl.AnchorY = 0;
}
public override bool OnTouchEvent(MotionEvent e)
{
if (e.PointerCount > 1)
{
IsPinching = true;
currentScale = lbl.Scale;
originalDistanceX = Math.Abs(e.GetX(0) - e.GetX(1));
originalDistanceY = Math.Abs(e.GetY(0) - e.GetY(1));
}
else
{
IsPinching = false;
}
return base.OnTouchEvent(e);
}
public override bool DispatchTouchEvent(Android.Views.MotionEvent e)
{
switch (e.Action)
{
case MotionEventActions.Down:
this.Parent.RequestDisallowInterceptTouchEvent(true);
break;
case MotionEventActions.Move:
if(IsPinching && e.PointerCount > 1)
{
currentdistanceX = Math.Abs(e.GetX(0) - e.GetX(1));
currentdistanceY = Math.Abs(e.GetY(0) - e.GetY(1));
if (originalDistanceX < currentdistanceX || originalDistanceY < currentdistanceY)
lbl.Scale = currentScale + 0.01;
else if (originalDistanceX > currentdistanceX || originalDistanceY > currentdistanceY)
lbl.Scale = currentScale - 0.01;
}
break;
case MotionEventActions.Up:
this.Parent.RequestDisallowInterceptTouchEvent(false);
break;
}
return base.DispatchTouchEvent(e);
}
}
in android I was able to achieve zoom to some extent but the scrolling in not smooth, but I compromised for it. Now the problem is the text is getting stripped in the label. Somebody please help me my app itself is for reading which is he basic feature not working.
Thanks in advance

I got it. Basically android label has no of lines limit of 100, i have to override it in the renderer
label.SetMaxLines(4000);

Related

Get scroll position of ViewCell inside ListView Xamarin Form

I would like to know the scroll position of ViewCell inside the ListView.
Tried with various ways but always that gives me 0 value.
My intension is to get ViewCell's position in screen. In order to resolve this problem trying to get it's scroll position and then i will add this value to the Y value of ListView object.
Can anybody please help me in this case?
you have to make custom renderer of the ViewCell its kinda tricky to send the positions to pcl then we subscribe to the event in the view here's my code
PCL
public class SAChatViewCell : ViewCell
{
public delegate int[] IntEventHandler(object sender, float[] postion);
public event IntEventHandler OnCellItemLongClicked;
public event EventHandler OnCellItemTouched;
public void InvokeOnCellItemLongClicked(object sender, float[] e)
{
//send the current grid
OnCellItemLongClicked?.Invoke(sender, e);
}
public void InvokeOnCellItemTouched(object sender, EventArgs e)
{
//send the current grid
OnCellItemTouched?.Invoke(sender, e);
}
}
Android Renderer
class SAChatViewCellRenderer : ViewCellRenderer
{
private bool selected;
ClickListener handler = new ClickListener();
static Android.Widget.ListView listView;
Xamarin.Forms.ListView listviewforms;
static SAChatViewCell cellElement;
Android.Views.View cellControl;
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, Android.Views.ViewGroup parent, Android.Content.Context context)
{
try
{
if (cellControl == null)
{
cellControl = base.GetCellCore(item, convertView, parent, context);
}
cellElement = item as SAChatViewCell;
selected = false;
listviewforms = cellElement.View.Parent.Parent as Xamarin.Forms.ListView;
if (listviewforms == null)
{
return null;
}
if (listviewforms.BackgroundColor.ToAndroid() == Color.Transparent.ToAndroid())
{
cellControl.SetBackgroundColor(Color.White.ToAndroid());
}
else
{
cellControl.SetBackgroundColor(listviewforms.BackgroundColor.ToAndroid());
}
cellControl.SetOnLongClickListener(handler);
cellControl.SetOnTouchListener(handler);
return cellControl;
}
catch (Exception ex)
{
return null;
}
}
protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs args)
{
base.OnCellPropertyChanged(sender, args);
if (args.PropertyName == "IsSelected")
{
// I had to create a property to track the selection because cellCore.Selected is always false.
// Toggle selection
selected = !selected;
var selectedBackground = cellElement.SelectedBackgroundColor.ToAndroid();
if (selected)
{
if (selectedBackground == Color.Transparent.ToAndroid())
{
cellControl.SetBackgroundColor(Color.White.ToAndroid());
return;
}
cellControl.SetBackgroundColor(selectedBackground);
}
else
{
if (listviewforms.BackgroundColor.ToAndroid() == Color.Transparent.ToAndroid())
{
cellControl.SetBackgroundColor(Color.White.ToAndroid());
}
else
{
cellControl.SetBackgroundColor(listviewforms.BackgroundColor.ToAndroid());
}
}
}
}
internal class ClickListener : Java.Lang.Object, IOnLongClickListener, IOnTouchListener
{
//event priority Touch - LongClick - Click
//NOTE: return true to indicate that we have handled the event and it should stop here;
public bool OnLongClick(Android.Views.View sender)
{
var cellItem = sender as INativeElementView;
var viewCell = sender as Android.Views.View;
float[] location = new float[] { 0, 0 };
Android.Views.View parentRow = (Android.Views.View)viewCell.Parent;
listView = (Android.Widget.ListView)parentRow.Parent;
int position = listView.GetPositionForView(parentRow);
var x = parentRow.Right;
var y = (parentRow.Top - listView.DividerHeight) <= 0 ? parentRow.Bottom : parentRow.Top;
int view_height = parentRow.Height;
location[0] = (x / MainActivity.Current.Resources.DisplayMetrics.Density);
location[1] = y / MainActivity.Current.Resources.DisplayMetrics.Density;
//send current cell
cellElement.InvokeOnCellItemLongClicked((cellItem.Element as ViewCell).View, location);
listView.Scroll += ListView_Scroll;
return true;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (listView != null)
{
listView.Scroll -= ListView_Scroll;
}
}
private void ListView_Scroll(object sender, Android.Widget.AbsListView.ScrollEventArgs e)
{
cellElement.InvokeOnCellItemTouched(cellElement.View, EventArgs.Empty);
}
//return false if you have not handled it and/or the event should continue to any other on-click listeners.
public bool OnTouch(Android.Views.View v, MotionEvent e)
{
if (e.Action == MotionEventActions.Down)
{
cellElement.InvokeOnCellItemTouched(cellElement.View, EventArgs.Empty);
//cellCore.SetOnTouchListener(this);
}
return false;
}
}
}
}
iOS Renderer
class SAUITableViewCell : UITableViewCell
{
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
base.TouchesBegan(touches, evt);
}
}
//When you scroll, your cells are created in real time. cells aren't created from scratch, instead iOS just takes a cell that has just left the screen and sends it through
class SAChatViewCellRenderer : ViewCellRenderer, IUIGestureRecognizerDelegate
{
UITableView TV;
SAChatViewCell cellElement;
public IntPtr Handle => new IntPtr();
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
try
{
UITableViewCell cell = base.GetCell(item, reusableCell, tv);
TV = tv;
var uiTapGestureRecognize = new UITapGestureRecognizer(OnClick);
var uiLongPressGestureRecognizer = new UILongPressGestureRecognizer(OnLongClick);
uiLongPressGestureRecognizer.MinimumPressDuration = 0.5;
cell.AddGestureRecognizer(uiTapGestureRecognize);
cell.AddGestureRecognizer(uiLongPressGestureRecognizer);
cellElement = item as SAChatViewCell;
cell.BackgroundColor = UIColor.Clear;
if (cellElement.SelectedBackgroundColor == Color.Transparent)
{
cell.SelectionStyle = UITableViewCellSelectionStyle.None;
}
else
{
cell.SelectedBackgroundView = new UIView
{
BackgroundColor = cellElement.SelectedBackgroundColor.ToUIColor() ?? default(UIColor)
};
}
return cell;
}
catch (Exception ex)
{
throw ex;
}
}
private void OnLongClick(UILongPressGestureRecognizer arg)
{
//get the current touch coords based on listview
CGPoint coords = arg.LocationInView(TV);
//current cell
if (arg.State != UIGestureRecognizerState.Began)
{
var indexPath = TV.IndexPathForRowAtPoint(coords);
CGRect Rect = TV.RectForRowAtIndexPath(indexPath);
//delete the listview offset
Rect.Offset(-TV.ContentOffset.X, -TV.ContentOffset.Y);
var CurrentViewCell = (arg.View as UIKit.UITableViewCell).Superview;
//Note : xamarin forms cell element MonoTouch creates it's own internal delegate type for UIScrollView so we either override the uiviewtable or trigger the ondisappear event
var cellItem = arg.View as INativeElementView;
(((cellItem.Element as ViewCell).Parent) as ListView).ItemDisappearing += (s, o) =>
{
cellElement.InvokeOnCellItemTouched(cellElement.View, EventArgs.Empty);
};
float[] location = new float[] { 0, 0 };
location[0] = (float)Rect.X;
var Y = Rect.Top <= 0 ? Rect.Bottom : Rect.Top;
location[1] = (float)Y;
cellElement.InvokeOnCellItemLongClicked((cellItem.Element as ViewCell).View, location);
}
}
private void OnClick()
{
cellElement.InvokeOnCellItemTouched(cellElement.View, EventArgs.Empty);
}
public void Dispose()
{
throw new NotImplementedException();
}
}
}
I have found a solution,
Problem :
My intension is to get ViewCell's position in screen
Solution :
Step 1 : Keep scrollview inside the Relative layout.
Step 2 : When user click on scroll view's ViewCell, save touch point (X, Y) of relative layout. In Y co-ordinate, add top position of relative layout so you will get touch point relative to whole screen.
Step 3 : When user click on scroll view's ViewCell, call XYZ() method.
Step 4 : Inside XYZ() method, do whatever functionality which required on (X, Y) co-ordinate. (Note : put 300ms delay in doing functionality in XYZ() method, as step-2 required some time in saving
touch points.)

How to implement ViewPagers with Dot Sliders in Xamarin.Android?

I am learning Xamarin.Android recently, I was trying to implement pageviewers with dot sliders for my practice from the last few days but failed to implement it, I had refereed many solutions from popular hubs like stackoverflow and github but failed to do so.
Can any one please explain me how to implement the viewpagers with dot sliders in xamarin.android.
I am attaching a photo to this for your reference, which would give you a clear view on my requirement.
as you can see he is sliding the pages using the dot sliders i was trying to do so.
Please explain me how to do it in Xamarin.Android (Native)
First define your own custom circle page-indicator let it be CirclePageIndicator.cs add this to your project (not activity file)
using Android.Content;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Graphics;
using Android.Support.V4.View;
using Android.Util;
using Java.Lang;
using Java.Interop;
namespace MyApplication.Droid.Library
{
public class CirclePageIndicator : View,PageIndicator
{
const int HORIZONTAL = 0;
const int VERTICAL = 1;
private float mRadius;
private Paint mPaintPageFill;
private Paint mPaintStroke;
private Paint mPaintFill;
private ViewPager mViewPager;
private ViewPager.IOnPageChangeListener mListener;
private int mCurrentPage;
private int mSnapPage;
private int mCurrentOffset;
private int mScrollState;
private int mPageSize;
private int mOrientation;
private bool mCentered;
private bool mSnap;
private const int INVALID_POINTER = -1;
private int mTouchSlop;
private float mLastMotionX = -1;
private int mActivePointerId = INVALID_POINTER;
private bool mIsDragging;
public CirclePageIndicator(Context context) : this(context, null)
{
}
public CirclePageIndicator(Context context, IAttributeSet attrs) : this(context, attrs, Resource.Attribute.vpiCirclePageIndicatorStyle)
{
}
public CirclePageIndicator(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
//Load defaults from resources
var res = Resources;
int defaultPageColor = res.GetColor(Resource.Color.default_circle_indicator_page_color);
int defaultFillColor = res.GetColor(Resource.Color.default_circle_indicator_fill_color);
int defaultOrientation = res.GetInteger(Resource.Integer.default_circle_indicator_orientation);
int defaultStrokeColor = res.GetColor(Resource.Color.default_circle_indicator_stroke_color);
float defaultStrokeWidth = res.GetDimension(Resource.Dimension.default_circle_indicator_stroke_width);
float defaultRadius = res.GetDimension(Resource.Dimension.default_circle_indicator_radius);
bool defaultCentered = res.GetBoolean(Resource.Boolean.default_circle_indicator_centered);
bool defaultSnap = res.GetBoolean(Resource.Boolean.default_circle_indicator_snap);
//Retrieve styles attributes
var a = context.ObtainStyledAttributes(attrs, Resource.Styleable.CirclePageIndicator, defStyle, Resource.Style.Widget_CirclePageIndicator);
mCentered = a.GetBoolean(Resource.Styleable.CirclePageIndicator_centered, defaultCentered);
mOrientation = a.GetInt(Resource.Styleable.CirclePageIndicator_orientation, defaultOrientation);
mPaintPageFill = new Paint(PaintFlags.AntiAlias);
mPaintPageFill.SetStyle(Paint.Style.Fill);
mPaintPageFill.Color = a.GetColor(Resource.Styleable.CirclePageIndicator_pageColor, defaultPageColor);
mPaintStroke = new Paint(PaintFlags.AntiAlias);
mPaintStroke.SetStyle(Paint.Style.Stroke);
mPaintStroke.Color = a.GetColor(Resource.Styleable.CirclePageIndicator_strokeColor, defaultStrokeColor);
mPaintStroke.StrokeWidth = a.GetDimension(Resource.Styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth);
mPaintFill = new Paint(PaintFlags.AntiAlias);
mPaintFill.SetStyle(Paint.Style.Fill);
mPaintFill.Color = a.GetColor(Resource.Styleable.CirclePageIndicator_fillColor, defaultFillColor);
mRadius = a.GetDimension(Resource.Styleable.CirclePageIndicator_radius, defaultRadius);
mSnap = a.GetBoolean(Resource.Styleable.CirclePageIndicator_snap, defaultSnap);
a.Recycle();
var configuration = ViewConfiguration.Get(context);
mTouchSlop = ViewConfigurationCompat.GetScaledPagingTouchSlop(configuration);
}
public void SetCentered(bool centered)
{
mCentered = centered;
Invalidate();
}
public bool IsCentered()
{
return mCentered;
}
public void SetPageColor(Color pageColor)
{
mPaintPageFill.Color = pageColor;
Invalidate();
}
public int GetPageColor()
{
return mPaintPageFill.Color;
}
public void SetFillColor(Color fillColor)
{
mPaintFill.Color = fillColor;
Invalidate();
}
public int GetFillColor()
{
return mPaintFill.Color;
}
public void setOrientation(int orientation)
{
switch (orientation)
{
case HORIZONTAL:
case VERTICAL:
mOrientation = orientation;
UpdatePageSize();
RequestLayout();
break;
default:
throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL.");
}
}
public int GetOrientation()
{
return mOrientation;
}
public void SetStrokeColor(Color strokeColor)
{
mPaintStroke.Color = strokeColor;
Invalidate();
}
public int GetStrokeColor()
{
return mPaintStroke.Color;
}
public void SetStrokeWidth(float strokeWidth)
{
mPaintStroke.StrokeWidth = strokeWidth;
Invalidate();
}
public float GetStrokeWidth()
{
return mPaintStroke.StrokeWidth;
}
public void SetRadius(float radius)
{
mRadius = radius;
Invalidate();
}
public float GetRadius()
{
return mRadius;
}
public void SetSnap(bool snap)
{
mSnap = snap;
Invalidate();
}
public bool IsSnap()
{
return mSnap;
}
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
if (mViewPager == null)
{
return;
}
int count = mViewPager.Adapter.Count;
if (count == 0)
{
return;
}
if (mCurrentPage >= count)
{
SetCurrentItem(count - 1);
return;
}
int longSize;
int longPaddingBefore;
int longPaddingAfter;
int shortPaddingBefore;
if (mOrientation == HORIZONTAL)
{
longSize = Width;
longPaddingBefore = PaddingLeft;
longPaddingAfter = PaddingRight;
shortPaddingBefore = PaddingTop;
}
else
{
longSize = Height;
longPaddingBefore = PaddingTop;
longPaddingAfter = PaddingBottom;
shortPaddingBefore = PaddingLeft;
}
float threeRadius = mRadius * 3;
float shortOffset = shortPaddingBefore + mRadius;
float longOffset = longPaddingBefore + mRadius;
if (mCentered)
{
longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((count * threeRadius) / 2.0f);
}
float dX;
float dY;
float pageFillRadius = mRadius;
if (mPaintStroke.StrokeWidth > 0)
{
pageFillRadius -= mPaintStroke.StrokeWidth / 2.0f;
}
//Draw stroked circles
for (int iLoop = 0; iLoop < count; iLoop++)
{
float drawLong = longOffset + (iLoop * threeRadius);
if (mOrientation == HORIZONTAL)
{
dX = drawLong;
dY = shortOffset;
}
else
{
dX = shortOffset;
dY = drawLong;
}
// Only paint fill if not completely transparent
if (mPaintPageFill.Alpha > 0)
{
canvas.DrawCircle(dX, dY, pageFillRadius, mPaintPageFill);
}
// Only paint stroke if a stroke width was non-zero
if (pageFillRadius != mRadius)
{
canvas.DrawCircle(dX, dY, mRadius, mPaintStroke);
}
}
//Draw the filled circle according to the current scroll
float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius;
if (!mSnap && (mPageSize != 0))
{
cx += (mCurrentOffset * 1.0f / mPageSize) * threeRadius;
}
if (mOrientation == HORIZONTAL)
{
dX = longOffset + cx;
dY = shortOffset;
}
else
{
dX = shortOffset;
dY = longOffset + cx;
}
canvas.DrawCircle(dX, dY, mRadius, mPaintFill);
}
public override bool OnTouchEvent(MotionEvent ev)
{
if (base.OnTouchEvent(ev))
{
return true;
}
if ((mViewPager == null) || (mViewPager.Adapter.Count == 0))
{
return false;
}
var action = ev.Action;
switch ((int)action & MotionEventCompat.ActionMask)
{
case (int)MotionEventActions.Down:
mActivePointerId = MotionEventCompat.GetPointerId(ev, 0);
mLastMotionX = ev.GetX();
break;
case (int)MotionEventActions.Move:
{
int activePointerIndex = MotionEventCompat.FindPointerIndex(ev, mActivePointerId);
float x = MotionEventCompat.GetX(ev, activePointerIndex);
float deltaX = x - mLastMotionX;
if (!mIsDragging)
{
if (Java.Lang.Math.Abs(deltaX) > mTouchSlop)
{
mIsDragging = true;
}
}
if (mIsDragging)
{
if (!mViewPager.IsFakeDragging)
{
mViewPager.BeginFakeDrag();
}
mLastMotionX = x;
mViewPager.FakeDragBy(deltaX);
}
break;
}
case (int)MotionEventActions.Cancel:
case (int)MotionEventActions.Up:
if (!mIsDragging)
{
int count = mViewPager.Adapter.Count;
int width = Width;
float halfWidth = width / 2f;
float sixthWidth = width / 6f;
if ((mCurrentPage > 0) && (ev.GetX() < halfWidth - sixthWidth))
{
mViewPager.CurrentItem = mCurrentPage - 1;
return true;
}
else if ((mCurrentPage < count - 1) && (ev.GetX() > halfWidth + sixthWidth))
{
mViewPager.CurrentItem = mCurrentPage + 1;
return true;
}
}
mIsDragging = false;
mActivePointerId = INVALID_POINTER;
if (mViewPager.IsFakeDragging)
mViewPager.EndFakeDrag();
break;
case MotionEventCompat.ActionPointerDown:
{
int index = MotionEventCompat.GetActionIndex(ev);
float x = MotionEventCompat.GetX(ev, index);
mLastMotionX = x;
mActivePointerId = MotionEventCompat.GetPointerId(ev, index);
break;
}
case MotionEventCompat.ActionPointerUp:
int pointerIndex = MotionEventCompat.GetActionIndex(ev);
int pointerId = MotionEventCompat.GetPointerId(ev, pointerIndex);
if (pointerId == mActivePointerId)
{
int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mActivePointerId = MotionEventCompat.GetPointerId(ev, newPointerIndex);
}
mLastMotionX = MotionEventCompat.GetX(ev, MotionEventCompat.FindPointerIndex(ev, mActivePointerId));
break;
}
return true;
}
public void SetViewPager(ViewPager view)
{
if (view.Adapter == null)
{
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = view;
mViewPager.SetOnPageChangeListener(this);
UpdatePageSize();
Invalidate();
}
private void UpdatePageSize()
{
if (mViewPager != null)
{
mPageSize = (mOrientation == HORIZONTAL) ? mViewPager.Width : mViewPager.Height;
}
}
public void SetViewPager(ViewPager view, int initialPosition)
{
SetViewPager(view);
SetCurrentItem(initialPosition);
}
public void SetCurrentItem(int item)
{
if (mViewPager == null)
{
throw new IllegalStateException("ViewPager has not been bound.");
}
mViewPager.CurrentItem = item;
mCurrentPage = item;
Invalidate();
}
public void NotifyDataSetChanged()
{
Invalidate();
}
public void OnPageScrollStateChanged(int state)
{
mScrollState = state;
if (mListener != null)
{
mListener.OnPageScrollStateChanged(state);
}
}
public void OnPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
mCurrentPage = position;
mCurrentOffset = positionOffsetPixels;
UpdatePageSize();
Invalidate();
if (mListener != null)
{
mListener.OnPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
public void OnPageSelected(int position)
{
if (mSnap || mScrollState == ViewPager.ScrollStateIdle)
{
mCurrentPage = position;
mSnapPage = position;
Invalidate();
}
if (mListener != null)
{
mListener.OnPageSelected(position);
}
}
public void SetOnPageChangeListener(ViewPager.IOnPageChangeListener listener)
{
mListener = listener;
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (mOrientation == HORIZONTAL)
{
SetMeasuredDimension(MeasureLong(widthMeasureSpec), MeasureShort(heightMeasureSpec));
}
else
{
SetMeasuredDimension(MeasureShort(widthMeasureSpec), MeasureLong(heightMeasureSpec));
}
}
/**
* Determines the width of this view
*
* #param measureSpec
* A measureSpec packed into an int
* #return The width of the view, honoring constraints from measureSpec
*/
private int MeasureLong(int measureSpec)
{
int result = 0;
var specMode = MeasureSpec.GetMode(measureSpec);
var specSize = MeasureSpec.GetSize(measureSpec);
if ((specMode == MeasureSpecMode.Exactly) || (mViewPager == null))
{
//We were told how big to be
result = specSize;
}
else
{
//Calculate the width according the views count
int count = mViewPager.Adapter.Count;
result = (int)(PaddingLeft + PaddingRight
+ (count * 2 * mRadius) + (count - 1) * mRadius + 1);
//Respect AT_MOST value if that was what is called for by measureSpec
if (specMode == MeasureSpecMode.AtMost)
{
result = Java.Lang.Math.Min(result, specSize);
}
}
return result;
}
/**
* Determines the height of this view
*
* #param measureSpec
* A measureSpec packed into an int
* #return The height of the view, honoring constraints from measureSpec
*/
private int MeasureShort(int measureSpec)
{
int result = 0;
var specMode = MeasureSpec.GetMode(measureSpec);
var specSize = MeasureSpec.GetSize(measureSpec);
if (specMode == MeasureSpecMode.Exactly)
{
//We were told how big to be
result = specSize;
}
else
{
//Measure the height
result = (int)(2 * mRadius + PaddingTop + PaddingBottom + 1);
//Respect AT_MOST value if that was what is called for by measureSpec
if (specMode == MeasureSpecMode.AtMost)
{
result = Java.Lang.Math.Min(result, specSize);
}
}
return result;
}
protected override void OnRestoreInstanceState(IParcelable state)
{
try
{
SavedState savedState = (SavedState)state;
base.OnRestoreInstanceState(savedState.SuperState);
mCurrentPage = savedState.CurrentPage;
mSnapPage = savedState.CurrentPage;
}
catch
{
base.OnRestoreInstanceState(state);
// Ignore, this needs to support IParcelable...
}
RequestLayout();
}
protected override IParcelable OnSaveInstanceState()
{
var superState = base.OnSaveInstanceState();
var savedState = new SavedState(superState);
savedState.CurrentPage = mCurrentPage;
return savedState;
}
public class SavedState : BaseSavedState
{
public int CurrentPage { get; set; }
public SavedState(IParcelable superState) : base(superState)
{
}
private SavedState(Parcel parcel) : base(parcel)
{
CurrentPage = parcel.ReadInt();
}
public override void WriteToParcel(Parcel dest, ParcelableWriteFlags flags)
{
base.WriteToParcel(dest, flags);
dest.WriteInt(CurrentPage);
}
[ExportField("CREATOR")]
static SavedStateCreator InitializeCreator()
{
return new SavedStateCreator();
}
class SavedStateCreator : Java.Lang.Object, IParcelableCreator
{
public Java.Lang.Object CreateFromParcel(Parcel source)
{
return new SavedState(source);
}
public Java.Lang.Object[] NewArray(int size)
{
return new SavedState[size];
}
}
}
}
}
Now add PageIndicator.cs file to your project (not activity file)
using Android.Support.V4.View;
namespace MyApplication.Droid.Library
{
public interface PageIndicator : ViewPager.IOnPageChangeListener
{
/**
* Bind the indicator to a ViewPager.
*
* #param view
*/
void SetViewPager(ViewPager view);
/**
* Bind the indicator to a ViewPager.
*
* #param view
* #param initialPosition
*/
void SetViewPager(ViewPager view, int initialPosition);
/**
* <p>Set the current page of both the ViewPager and indicator.</p>
*
* <p>This <strong>must</strong> be used if you need to set the page before
* the views are drawn on screen (e.g., default start page).</p>
*
* #param item
*/
void SetCurrentItem(int item);
/**
* Set a page change listener which will receive forwarded events.
*
* #param listener
*/
void SetOnPageChangeListener(ViewPager.IOnPageChangeListener listener);
/**
* Notify the indicator that the fragment list has changed.
*/
void NotifyDataSetChanged();
}
}
Now add the following files to your values file:
vpi__styles.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<style name="Widget"></style>
<style name="Widget.CirclePageIndicator" parent="Widget">
<item name="centered">#bool/default_circle_indicator_centered</item>
<item name="fillColor">#color/default_circle_indicator_fill_color</item>
<item name="pageColor">#color/default_circle_indicator_page_color</item>
<item name="orientation">#integer/default_circle_indicator_orientation</item>
<item name="radius">#dimen/default_circle_indicator_radius</item>
<item name="snap">#bool/default_circle_indicator_snap</item>
<item name="strokeColor">#color/default_circle_indicator_stroke_color</item>
<item name="strokeWidth">#dimen/default_circle_indicator_stroke_width</item>
</style>
</resources>
vpi__defaults.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<bool name="default_circle_indicator_centered">true</bool>
<color name="default_circle_indicator_fill_color">#FFFFFFFF</color>
<color name="default_circle_indicator_page_color">#00000000</color>
<integer name="default_circle_indicator_orientation">0</integer>
<dimen name="default_circle_indicator_radius">3dp</dimen>
<bool name="default_circle_indicator_snap">false</bool>
<color name="default_circle_indicator_stroke_color">#FFDDDDDD</color>
<dimen name="default_circle_indicator_stroke_width">1dp</dimen>
</resources>
vpi__colors.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<color name="vpi__background_holo_dark">#ff000000</color>
<color name="vpi__background_holo_light">#fff3f3f3</color>
<color name="vpi__bright_foreground_disabled_holo_dark">#ff4c4c4c</color>
<color name="vpi__bright_foreground_disabled_holo_light">#ffb2b2b2</color>
</resources>
vpi__attrs.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<declare-styleable name="ViewPagerIndicator">
<!-- Style of the circle indicator. -->
<attr name="vpiCirclePageIndicatorStyle" format="reference"/>
</declare-styleable>
<declare-styleable name="CirclePageIndicator">
<!-- Whether or not the indicators should be centered. -->
<attr name="centered" format="boolean" />
<!-- Color of the filled circle that represents the current page. -->
<attr name="fillColor" format="color" />
<!-- Color of the filled circles that represents pages. -->
<attr name="pageColor" format="color" />
<!-- Orientation of the indicator. -->
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
<!-- Radius of the circles. This is also the spacing between circles. -->
<attr name="radius" format="dimension" />
<!-- Whether or not the selected indicator snaps to the circles. -->
<attr name="snap" format="boolean" />
<!-- Color of the open circles. -->
<attr name="strokeColor" format="color" />
<!-- Width of the stroke used to draw the circles. -->
<attr name="strokeWidth" format="dimension" />
</declare-styleable>
</resources>
Now add this code to design file.
Here you have to notice that MyApplication.Droid.Library is my name space and CirclePageIndicator is my page indicator file name so replace it with your's
<MyApplication.Droid.Library.CirclePageIndicator
android:id="#+id/indicator"
android:padding="10dip"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
/>
now add the following in the activity file.
using Android.Support.V4.App;
using MyApplication.Droid.Library;
//global
public ViewPager mPager;
public PageIndicator mIndicator;
//in on create
var indicator = FindViewById<CirclePageIndicator>(Resource.Id.indicator);
mIndicator = indicator;
indicator.SetViewPager(mPager);
indicator.SetSnap(true);
I had solved this from the github
Look at this the sample project 'Android-ViewPagerIndicator to Xamarin.Android'. You need to include the library, where there are multiple implementation to display the indicators on viewpager.
https://github.com/Cheesebaron/ViewPagerIndicator
I got it working just like iOS PageControl.
Create the custom class as such:
using Android.Content;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Runtime;
using Android.Util;
using Android.Widget;
using System;
using System.Collections.Generic;
namespace myProject.CustomWidgets
{
public class PageControl : LinearLayout
{
#region Properties
private int _currentPage = 0;
public int CurrentPage
{
get
{
return _currentPage;
}
set
{
_currentPage = value;
SetCurrentPageIndicator();
}
}
public int Pages = 0;
public Color PageIndicatorTintColor = ColorHelper.Clear;
public Color CurrentPageIndicatorTintColor = ColorHelper.Clear;
private readonly Context context;
private List<ImageView> ivList = new List<ImageView>();
private List<Drawable> drawableList = new List<Drawable>();
private readonly int circleSize = 7;
#endregion Properties
#region Constructor
protected PageControl(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public PageControl(Context context) : base(context)
{
this.context = context;
InitConfig();
}
public PageControl(Context context, IAttributeSet attrs) : base(context, attrs)
{
this.context = context;
InitConfig();
}
public PageControl(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
this.context = context;
InitConfig();
}
public PageControl(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
{
this.context = context;
InitConfig();
}
#endregion Constructor
#region Methods
private void InitConfig()
{
Orientation = Orientation.Horizontal;
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
Render();
}
private void Render()
{
// Start with a clean slate
RemoveAllViews();
ivList = new List<ImageView>();
drawableList = new List<Drawable>();
if (Pages <= 0)
{
return;
}
for (int i = 0; i < Pages; i++)
{
var iv = new ImageView(context);
var size = ConvertionHelper.DensityToPixels(context, circleSize);
var margin = (int)(size / 2.5);
var lp = new LayoutParams(size, size);
lp.SetMargins(margin, 0, margin, 0);
iv.LayoutParameters = lp;
/* By default, all drawables instances loaded from the same resource share a common state.
* If you modify the state of one instance, all the other instances will receive the same modification.
* Calling this method on a mutable Drawable will have no effect */
var drawable = ResourcesHelper.GetDrawable(context, Resource.Drawable.ic_circle_separator).Mutate();
drawable.SetColorFilter(PageIndicatorTintColor, PorterDuff.Mode.SrcAtop);
iv.SetImageDrawable(drawable);
drawableList.Add(drawable);
ivList.Add(iv);
AddView(iv);
}
// Initial current page indicator set-up
SetCurrentPageIndicator();
}
private void SetCurrentPageIndicator()
{
if (ivList.Count == 0 || drawableList.Count == 0 || ivList.Count != drawableList.Count ||
CurrentPage < 0 || CurrentPage >= ivList.Count)
{
return;
}
// Reset all colors
for (int i = 0; i < ivList.Count; i++)
{
drawableList[i].SetColorFilter(PageIndicatorTintColor, PorterDuff.Mode.SrcIn);
ivList[i].SetImageDrawable(drawableList[i]);
}
// Change color of current page indicator
drawableList[CurrentPage].SetColorFilter(CurrentPageIndicatorTintColor, PorterDuff.Mode.SrcIn);
ivList[CurrentPage].SetImageDrawable(drawableList[CurrentPage]);
}
#endregion Methods
}
}
Then use on your xml file such as:
<myProject.PageControl
android:id="#+id/pagecontrol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
Then on your activity you would have:
var pagecontrol = FindViewById<PageControl>(Resource.Id.pagecontrol);
pagecontrol.PageIndicatorTintColor = ColorHelper.PositiveBlue;
pagecontrol.CurrentPageIndicatorTintColor = ColorHelper.Orange;
pagecontrol.CurrentPage = 0;
pagecontrol.Pages = 3;

Xna (MonoGame) DynamicSoundEffectInstance Buffer already Full exception

I'm making this game in MonoGame (basically Xna) that uses DynamicSoundEffectInstance class. MonoGame does not have an implementation of DynamicSoundEffectInstance yet, so I made my own:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if MONOMAC
using MonoMac.OpenAL;
#else
using OpenTK.Audio.OpenAL;
#endif
using System.Threading;
namespace Microsoft.Xna.Framework.Audio
{
public sealed class DynamicSoundEffectInstance : IDisposable
{
private const int BUFFERCOUNT = 2;
private SoundState soundState = SoundState.Stopped;
private AudioChannels channels;
private int sampleRate;
private ALFormat format;
private bool looped = false;
private float volume = 1.0f;
private float pan = 0;
private float pitch = 0f;
private int sourceId;
private int[] bufferIds;
private int[] bufferIdsToFill;
private int currentBufferToFill;
private bool isDisposed = false;
private bool hasSourceId = false;
private Thread bufferFillerThread = null;
// Events
public event EventHandler<EventArgs> BufferNeeded;
internal void OnBufferNeeded(EventArgs args)
{
if (BufferNeeded != null)
{
BufferNeeded(this, args);
}
}
public DynamicSoundEffectInstance(int sampleRate, AudioChannels channels)
{
this.sampleRate = sampleRate;
this.channels = channels;
switch (channels)
{
case AudioChannels.Mono:
this.format = ALFormat.Mono16;
break;
case AudioChannels.Stereo:
this.format = ALFormat.Stereo16;
break;
default:
break;
}
}
public bool IsDisposed
{
get
{
return isDisposed;
}
}
public float Pan
{
get
{
return pan;
}
set
{
pan = value;
if (hasSourceId)
{
// Listener
// Pan
AL.Source(sourceId, ALSource3f.Position, pan, 0.0f, 0.1f);
}
}
}
public float Pitch
{
get
{
return pitch;
}
set
{
pitch = value;
if (hasSourceId)
{
// Pitch
AL.Source(sourceId, ALSourcef.Pitch, XnaPitchToAlPitch(pitch));
}
}
}
public float Volume
{
get
{
return volume;
}
set
{
volume = value;
if (hasSourceId)
{
// Volume
AL.Source(sourceId, ALSourcef.Gain, volume * SoundEffect.MasterVolume);
}
}
}
public SoundState State
{
get
{
return soundState;
}
}
private float XnaPitchToAlPitch(float pitch)
{
// pitch is different in XNA and OpenAL. XNA has a pitch between -1 and 1 for one octave down/up.
// openAL uses 0.5 to 2 for one octave down/up, while 1 is the default. The default value of 0 would make it completely silent.
return (float)Math.Exp(0.69314718 * pitch);
}
public void Play()
{
if (!hasSourceId)
{
bufferIds = AL.GenBuffers(BUFFERCOUNT);
sourceId = AL.GenSource();
hasSourceId = true;
}
soundState = SoundState.Playing;
if (bufferFillerThread == null)
{
bufferIdsToFill = bufferIds;
currentBufferToFill = 0;
OnBufferNeeded(EventArgs.Empty);
bufferFillerThread = new Thread(new ThreadStart(BufferFiller));
bufferFillerThread.Start();
}
AL.SourcePlay(sourceId);
}
public void Apply3D(AudioListener listener, AudioEmitter emitter)
{
Apply3D(new AudioListener[] { listener }, emitter);
}
public void Pause()
{
if (hasSourceId)
{
AL.SourcePause(sourceId);
soundState = SoundState.Paused;
}
}
public void Apply3D(AudioListener[] listeners, AudioEmitter emitter)
{
// get AL's listener position
float x, y, z;
AL.GetListener(ALListener3f.Position, out x, out y, out z);
for (int i = 0; i < listeners.Length; i++)
{
AudioListener listener = listeners[i];
// get the emitter offset from origin
Vector3 posOffset = emitter.Position - listener.Position;
// set up orientation matrix
Matrix orientation = Matrix.CreateWorld(Vector3.Zero, listener.Forward, listener.Up);
// set up our final position and velocity according to orientation of listener
Vector3 finalPos = new Vector3(x + posOffset.X, y + posOffset.Y, z + posOffset.Z);
finalPos = Vector3.Transform(finalPos, orientation);
Vector3 finalVel = emitter.Velocity;
finalVel = Vector3.Transform(finalVel, orientation);
// set the position based on relative positon
AL.Source(sourceId, ALSource3f.Position, finalPos.X, finalPos.Y, finalPos.Z);
AL.Source(sourceId, ALSource3f.Velocity, finalVel.X, finalVel.Y, finalVel.Z);
}
}
public void Dispose()
{
if (!isDisposed)
{
Stop(true);
AL.DeleteBuffers(bufferIds);
AL.DeleteSource(sourceId);
bufferIdsToFill = null;
hasSourceId = false;
isDisposed = true;
}
}
public void Stop()
{
if (hasSourceId)
{
AL.SourceStop(sourceId);
int pendingBuffers = PendingBufferCount;
if(pendingBuffers > 0)
AL.SourceUnqueueBuffers(sourceId, PendingBufferCount);
if (bufferFillerThread != null)
bufferFillerThread.Abort();
bufferFillerThread = null;
}
soundState = SoundState.Stopped;
}
public void Stop(bool immediate)
{
Stop();
}
public TimeSpan GetSampleDuration(int sizeInBytes)
{
throw new NotImplementedException();
}
public int GetSampleSizeInBytes(TimeSpan duration)
{
int size = (int)(duration.TotalMilliseconds * ((float)sampleRate / 1000.0f));
return (size + (size & 1)) * 16;
}
public void SubmitBuffer(byte[] buffer)
{
this.SubmitBuffer(buffer, 0, buffer.Length);
}
public void SubmitBuffer(byte[] buffer, int offset, int count)
{
if (bufferIdsToFill != null) {
AL.BufferData (bufferIdsToFill [currentBufferToFill], format, buffer, count, sampleRate);
AL.SourceQueueBuffer (sourceId, bufferIdsToFill [currentBufferToFill]);
currentBufferToFill++;
if (currentBufferToFill >= bufferIdsToFill.Length)
bufferIdsToFill = null;
else
OnBufferNeeded (EventArgs.Empty);
} else {
throw new Exception ("Buffer already full.");
}
}
private void BufferFiller()
{
bool done = false;
while (!done)
{
var state = AL.GetSourceState(sourceId);
if (state == ALSourceState.Stopped || state == ALSourceState.Initial)
AL.SourcePlay(sourceId);
if (bufferIdsToFill != null)
continue;
int buffersProcessed;
AL.GetSource(sourceId, ALGetSourcei.BuffersProcessed, out buffersProcessed);
if (buffersProcessed == 0)
continue;
bufferIdsToFill = AL.SourceUnqueueBuffers(sourceId, buffersProcessed);
currentBufferToFill = 0;
OnBufferNeeded(EventArgs.Empty);
}
}
public bool IsLooped
{
get
{
return looped;
}
set
{
looped = value;
}
}
public int PendingBufferCount
{
get
{
if (hasSourceId)
{
int buffersQueued;
AL.GetSource(sourceId, ALGetSourcei.BuffersQueued, out buffersQueued);
return buffersQueued;
}
return 0;
}
}
}
}
Now, I followed this tutorial on making dynamic sounds in Xna, which worked with my custom MonoGame class. However, when I run the project (Xamarin Studio 4, Mac OS X 10.8, with MonoGame 3.0.1), it throws this exception:
Buffer already full
Pointing at the code in my custom class:
public void SubmitBuffer(byte[] buffer, int offset, int count)
{
if (bufferIdsToFill != null) {
AL.BufferData (bufferIdsToFill [currentBufferToFill], format, buffer, count, sampleRate);
AL.SourceQueueBuffer (sourceId, bufferIdsToFill [currentBufferToFill]);
currentBufferToFill++;
if (currentBufferToFill >= bufferIdsToFill.Length)
bufferIdsToFill = null;
else
OnBufferNeeded (EventArgs.Empty);
} else {
throw new Exception ("Buffer already full."); //RIGHT HERE IS THE EXCEPTION
}
}
I commented out the exception, and ran it again. It played the sound, with pops in it, but it still played it. How can I clear the buffer, so it is not full? I followed this tutorial EXACTLY, so all the code I added to my project is in there.
Oh! Figured it out myself; I changed the pending buffer count from 3 to 2. My final submit buffer code was:
while(_instance.PendingBufferCount < 2)
SubmitBuffer();
Where the 2 is, used to be a 3. Now it no longer throws the exception.

Changing the colour of UIPageControl dots in MonoTouch

I was wondering if MonoTouch allows the developer to change the colour of UIPageControl dots to suit a light background - they seem to be fixed white, which makes them very hard to see on a light textured background.
I am aware there is no public API available for this but I was wondering if anything was internally implemented in MonoTouch to improve on this.
Otherwise, what's the recommended approach to using a UIPageControl on a light background?
I took a stab at translating this. I'm not sure it will work, but it does compile. Note that the page linked to contains comments indicating that Apple frowns upon this code and may reject it:
using System;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace Whatever
{
public class StyledPageControl : UIPageControl
{
public StyledPageControl () : base()
{
}
public override int CurrentPage {
get {
return base.CurrentPage;
}
set {
base.CurrentPage = value;
string imgActive = NSBundle.MainBundle.PathForResource("activeImage", "png");
string imgInactive = NSBundle.MainBundle.PathForResource("inactiveImage", "png");
for (int subviewIndex = 0; subviewIndex < this.Subviews.Length; subviewIndex++)
{
UIImageView subview = this.Subviews[subviewIndex] as UIImageView;
if (subviewIndex == value)
subview.Image = UIImage.FromFile(imgActive);
else
subview.Image = UIImage.FromFile(imgInactive);
}
}
}
public override int Pages {
get {
return base.Pages;
}
set {
base.Pages = value;
string img = NSBundle.MainBundle.PathForResource("inactiveImage", "png");
for (int subviewIndex = 0; subviewIndex < this.Subviews.Length; subviewIndex++)
{
UIImageView subview = this.Subviews[subviewIndex] as UIImageView;
subview.Image = UIImage.FromFile(img);
}
}
}
}
}
Convert the following code to C#: http://apptech.next-munich.com/2010/04/customizing-uipagecontrols-looks.html
I combined this and this for monotouch. I hope this helps.
The usage is like this:
_pager.Change += delegate(object sender, EventArgs e) {
var pc = sender as PageControl;
Console.WriteLine ("Change Delegate== " + pc.currentPage);
var toPage = pc.currentPage;
var pageOffset = _scroll.Frame.Width*toPage;
PointF p = new PointF(pageOffset, 0);
Console.WriteLine (pageOffset);
_scroll.SetContentOffset(p,true);
};
And the class like this.
public class PageControl:UIView
{
#region ctor
public PageControl (RectangleF rect) :base(rect)
{
this.BackgroundColor = UIColor.Red;
this.CurrenColor=new CGColor(.2f,15f,10F);
this.OtherColor =new CGColor(.77F,.71F,.60F);
}
#endregion
#region Fields
float kDotDiameter= 7.0f;
float kDotSpacer = 7.0f;
int _currentPage;
int _numberOfPages;
CGColor CurrenColor{get;set;}
CGColor OtherColor{get;set;}
public int currentPage
{
set
{
_currentPage = Math.Min(Math.Max(0, value),_numberOfPages-1);
this.SetNeedsDisplay();
}
get{return _currentPage;}
}
public int numberOfPages
{
set
{
_numberOfPages = Math.Max(0,value);
_currentPage = Math.Min(Math.Max(0, _currentPage), _numberOfPages-1);
this.SetNeedsDisplay();
}
get{return _numberOfPages;}
}
#endregion
#region Overrides
public override void Draw (RectangleF rect)
{
base.Draw (rect);
CGContext context = UIGraphics.GetCurrentContext();
context.SetAllowsAntialiasing(true);
RectangleF currentBounds = this.Bounds;
float dotsWidth = this.numberOfPages*kDotDiameter + Math.Max(0,this.numberOfPages-1)*kDotSpacer;
float x = currentBounds.GetMidX() - dotsWidth/2;
float y = currentBounds.GetMidY() - kDotDiameter/2;
for (int i = 0; i < _numberOfPages; i++) {
RectangleF circleRect = new RectangleF(x,y,kDotDiameter,kDotDiameter);
if (i==_currentPage) {
context.SetFillColor(this.CurrenColor);
}
else {
context.SetFillColor(this.OtherColor);
}
context.FillEllipseInRect(circleRect);
x += kDotDiameter + kDotSpacer;
}
}
public override void TouchesBegan (MonoTouch.Foundation.NSSet touches, UIEvent evt)
{
base.TouchesBegan (touches, evt);
PointF touchpoint = (touches.AnyObject as MonoTouch.UIKit.UITouch).LocationInView(this);
RectangleF currentbounds = this.Bounds;
float x = touchpoint.X- currentbounds.GetMidX();
if (x<0 && this.currentPage>=0) {
this.currentPage--;
Change(this,EventArgs.Empty);
}
else if (x>0 && this.currentPage<this.numberOfPages-1) {
this.currentPage++;
Change(this,EventArgs.Empty);
}
}
#endregion
#region delegate
public event EventHandler Change;
#endregion
}

Blackberry: How can I make a buttonfield act like toggle button?

I have a single button which I want to use as Start/Stop button. How can I make the buttonfield work as toggle button?
Please help.
Just change button label on fieldChange or navigationClick or touchEvent, don't forget to save toggle state in class member:
class ToggleButtonField extends ButtonField {
int mToggleState = -1;
String[] mLabels = {};
public ToggleButtonField(String[] labels) {
super(CONSUME_CLICK);
if(labels != null && labels.length > 0)
{
mLabels = labels;
mToggleState = 0;
updateLabel();
}
}
private void updateLabel() {
setLabel(mLabels[mToggleState]);
}
protected void fieldChangeNotify(int context) {
mToggleState = getNextToggleState(mToggleState);
updateLabel();
super.fieldChangeNotify(context);
}
private int getNextToggleState(int state) {
int result = mToggleState+1;
if(result >= mLabels.length)
result = 0;
return result;
}
}

Resources