Xamarin Android: как создать панель отмены

Я хочу знать, как создать панель отмены для Android Xamarin примерно так.

Я использовал этот код, созданный Dan Ardelean для Xamarin Android, который основывает код из примера Roman Nurik для Android, но он не работает для меня. Я получаю это сообщение:

Невозможно активировать экземпляр типа SwipeDismissListView.MainActivity из собственного дескриптора 0x200019 (key_handle 0xf9f4bcb). --->

System.MissingMethodException: No constructor found for SwipeDismissListView.MainActivity::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership) ---> Java.Interop.JavaLocationException: Exception of type 'Java.Interop.JavaLocationException' was thrown. --- End of inner exception stack trace --- at Java.Interop.TypeManager.CreateProxy (System.Type type, System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer) [0x00054] in <d855bac285f44dda8a0d8510b679b1e2>:0 at Java.Interop.TypeManager.CreateInstance (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer, System.Type targetType) [0x00111] in <d855bac285f44dda8a0d8510b679b1e2>:0 --- End of inner exception stack trace --- at Java.Interop.TypeManager.CreateInstance (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer, System.Type targetType) [0x0017d] in <d855bac285f44dda8a0d8510b679b1e2>:0 at Java.Lang.Object.GetObject (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer, System.Type type) [0x000b9] in <d855bac285f44dda8a0d8510b679b1e2>:0 at Java.Lang.Object._GetObject[T] (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer) [0x00017] in <d855bac285f44dda8a0d8510b679b1e2>:0 at Java.Lang.Object.GetObject[T] (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer) [0x00000] in <d855bac285f44dda8a0d8510b679b1e2>:0 at Android.Views.View.get_Context () [0x0001f] in <d855bac285f44dda8a0d8510b679b1e2>:0 at XpressCode.SwipeDismissListViewTouchListener..ctor (Android.Widget.ListView listView, XpressCode.IDismissCallbacks callbacks) [0x00021] in D:\...\Xamarin-SwipeToDismiss-master\SwipeDismissListView\SwipeDismissListViewTouchListener.cs:58 at SwipeDismissListView.MainActivity.OnCreate (Android.OS.Bundle bundle) [0x00086]

Я искал в Интернете примеры кода, но я не нашел ничего подобного.

Я пытался добавить блок-цитату конструктора,

public MainActivity()
{

}

но я получаю ошибку:

{System.InvalidCastException: Specified cast is not valid.
  at Android.Widget.AbsListView.SetOnScrollListener (Android.Widget.AbsListView+IOnScrollListener l) [0x0000c] in <d855bac285f44dda8a0d8510b679b1e2>:0 
  at SwipeDismissListView.MainActivity.OnCreate (Android.OS.Bundle bundle) [0x000a0] in D:\...\Xamarin-SwipeToDismiss-master\SwipeDismissListView\MainActivity.cs:45 }

Затем я удалил добавленный конструктор и добавил блок try... cacth. Приложение перерыв в этой строке:

list.SetOnScrollListener(touchListener.MakeScrollListener());

ЭТО КОДЕКС ССЫЛКИ:

MainActivity.cs

using System;

using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using System.Collections.Generic;
using XpressCode;


namespace SwipeDismissListView
{
    [Activity (Label = "SwipeDismissListView", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity,IDismissCallbacks,IUndoListener
    {
        UndoBarController mUndoBarController;
        ArrayAdapter<String> mAdapter;
        ListView list;

        protected override void OnCreate (Bundle bundle)
        {
            try
            {
                base.OnCreate(bundle);

                // Set our view from the "main" layout resource
                SetContentView(Resource.Layout.Main);

                String[] items = new String[20];
                for (int i = 0; i < items.Length; i++)
                {
                    items[i] = "Item " + (i + 1);
                }

                list = FindViewById<ListView>(Resource.Id.lstItems);

                mAdapter = new ArrayAdapter<String>(this, Android.Resource.Layout.SimpleListItem1, Android.Resource.Id.Text1, new List<String>(items));
                list.Adapter = mAdapter;

                SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(list, this);

                list.SetOnTouchListener(touchListener);
                list.SetOnScrollListener(touchListener.MakeScrollListener());

                mUndoBarController = new UndoBarController(FindViewById(Resource.Id.undobar), this);
            }
            catch (Exception ex)
            {
                Toast.MakeText(this,"error: " + ex,ToastLength.Short);
            }

        }

        protected override void OnSaveInstanceState (Bundle outState)
        {
            base.OnSaveInstanceState (outState);
            mUndoBarController.onSaveInstanceState(outState);
        }

        protected override void OnRestoreInstanceState (Bundle savedInstanceState)
        {
            base.OnRestoreInstanceState (savedInstanceState);
            mUndoBarController.onRestoreInstanceState(savedInstanceState);
        }

        public bool canDismiss (int position)
        {
            return true;
        }
        int _position;  
        public void onDismiss (ListView listView, int[] reverseSortedPositions)
        {
            foreach (int position in reverseSortedPositions) {
                var item = mAdapter.GetItem (position);
                mAdapter.Remove(item);
                _position = position;
                mUndoBarController.ShowUndoBar(false,GetString(Resource.String.undobar_sample_message),item);
            }
            mAdapter.NotifyDataSetChanged();
        }

        public void OnUndo(object token) {
            mAdapter.Insert ((string)token, _position);
            // Perform the undo
        }

    }
}

SwipeDismissListViewTouchListener.cs

using System;
using Android.Views;
using Android.Widget;
using System.Collections.Generic;
using Android.Graphics;
using Android.Animation;
using Java.Util;
using Android.OS;

namespace XpressCode
{
    public interface IDismissCallbacks
    {
        /**
         * Called to determine whether the given position can be dismissed.
         */
        bool canDismiss (int position);

        /**
         * Called when the user has indicated they she would like to dismiss one or more list item
         * positions.
         *
         * @param listView               The originating {@link ListView}.
         * @param reverseSortedPositions An array of positions to dismiss, sorted in descending
         *                               order for convenience.
         */
        void onDismiss (ListView listView, int[] reverseSortedPositions);
    }

    public class SwipeDismissListViewTouchListener:Java.Lang.Object,Android.Views.View.IOnTouchListener
    {
        // Cached ViewConfiguration and system-wide constant values
        int mSlop;
        int mMinFlingVelocity;
        int mMaxFlingVelocity;
        long mAnimationTime;

        // Fixed properties
        internal ListView mListView;
        internal IDismissCallbacks mCallbacks;
        internal int mViewWidth = 1;


        // Transient properties
        internal List<PendingDismissData> mPendingDismisses = new List<PendingDismissData> ();
        internal int mDismissAnimationRefCount = 0;
        internal float mDownX;
        internal float mDownY;
        internal bool mSwiping;
        internal int mSwipingSlop; 
        internal VelocityTracker mVelocityTracker;
        internal int mDownPosition;
        internal View mDownView;
        internal bool mPaused;

        public SwipeDismissListViewTouchListener (ListView listView, IDismissCallbacks callbacks)
        {
            ViewConfiguration vc = ViewConfiguration.Get (listView.Context);
            mSlop = vc.ScaledTouchSlop;
            mMinFlingVelocity = vc.ScaledMinimumFlingVelocity * 16;
            mMaxFlingVelocity = vc.ScaledMaximumFlingVelocity;
            mAnimationTime = listView.Context.Resources.GetInteger (Android.Resource.Integer.ConfigShortAnimTime);
            mListView = listView;
            mCallbacks = callbacks;
        }

        /**
     * Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures.
     *
     * @param enabled Whether or not to watch for gestures.
     */
        public void SetEnabled (bool enabled)
        {
            mPaused = !enabled;
        }


        /**
     * Returns an {@link android.widget.AbsListView.OnScrollListener} to be added to the {@link
     * ListView} using {@link ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)}.
     * If a scroll listener is already assigned, the caller should still pass scroll changes through
     * to this listener. This will ensure that this {@link SwipeDismissListViewTouchListener} is
     * paused during list view scrolling.</p>
     *
     * @see SwipeDismissListViewTouchListener
     */
        public AbsListView.IOnScrollListener MakeScrollListener() {

            return new OnScrollListener (this);
        }

        class OnScrollListener:AbsListView.IOnScrollListener
        {
            SwipeDismissListViewTouchListener _listener;
            public OnScrollListener(SwipeDismissListViewTouchListener listener)
            {
                _listener = listener;
            }

            public void OnScroll (AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
            {

            }
            public void OnScrollStateChanged (AbsListView view, ScrollState scrollState)
            {
                _listener.SetEnabled(scrollState != ScrollState.TouchScroll);
            }
            public void Dispose ()
            {
                throw new NotImplementedException ();
            }
            public IntPtr Handle {
                get { 
                    return new IntPtr ();
                }
            }
        }



        public bool OnTouch(View view, MotionEvent motionEvent)
        {
            if (mViewWidth < 2) {
                mViewWidth = mListView.Width;
            }

            switch (motionEvent.ActionMasked) {
            case MotionEventActions.Down:
                if (mPaused) {
                    return false;
                }
                // TODO: ensure this is a finger, and set a flag
                // Find the child view that was touched (perform a hit test)
                Rect rect = new Rect ();
                int childCount = mListView.ChildCount;
                int[] listViewCoords = new int[2];
                mListView.GetLocationOnScreen (listViewCoords);
                int x = (int)motionEvent.RawX - listViewCoords [0];
                int y = (int)motionEvent.RawY - listViewCoords [1];
                View child;
                for (int i = 0; i < childCount; i++) {
                    child = mListView.GetChildAt (i);
                    child.GetHitRect (rect);
                    if (rect.Contains (x, y)) {
                        mDownView = child;
                        break;
                    }
                }

                if (mDownView != null) {
                    mDownX = motionEvent.RawX;
                    mDownY = motionEvent.RawY;
                    mDownPosition = mListView.GetPositionForView (mDownView);
                    if (mCallbacks.canDismiss (mDownPosition)) {
                        mVelocityTracker = VelocityTracker.Obtain ();
                        mVelocityTracker.AddMovement (motionEvent);
                    } else {
                        mDownView = null;
                    }
                }
                return false;

            case MotionEventActions.Cancel: {
                    if (mVelocityTracker == null) {
                        break;
                    }

                    if (mDownView != null && mSwiping) {
                        // cancel
                        mDownView.Animate()
                            .TranslationX(0)
                            .Alpha(1)
                            .SetDuration(mAnimationTime)
                            .SetListener(null);
                    }
                    mVelocityTracker.Recycle();
                    mVelocityTracker = null;
                    mDownX = 0;
                    mDownY = 0;
                    mDownView = null;
                    mDownPosition = ListView.InvalidPosition;
                    mSwiping = false;
                    break;
                }

            case MotionEventActions.Up: {
                    if (mVelocityTracker == null) {
                        break;
                    }

                    float deltaX = motionEvent.RawX - mDownX;
                    mVelocityTracker.AddMovement(motionEvent);
                    mVelocityTracker.ComputeCurrentVelocity(1000);
                    float velocityX = mVelocityTracker.XVelocity;
                    float absVelocityX = Math.Abs(velocityX);
                    float absVelocityY = Math.Abs(mVelocityTracker.YVelocity);
                    bool dismiss = false;
                    bool dismissRight = false;
                    if (Math.Abs(deltaX) > mViewWidth / 2) {
                        dismiss = true;
                        dismissRight = deltaX > 0;
                    } else
                        if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity&& absVelocityY < absVelocityX&&mSwiping) {
                        // dismiss only if flinging in the same direction as dragging
                        dismiss = (velocityX < 0) == (deltaX < 0);
                        dismissRight = mVelocityTracker.XVelocity > 0;
                    }
                    if (dismiss&& mDownPosition != ListView.InvalidPosition) {
                        // dismiss
                        View downView = mDownView; // mDownView gets null'd before animation ends
                        int downPosition = mDownPosition;
                        ++mDismissAnimationRefCount;
                        var anim = mDownView.Animate ()
                            .TranslationX (dismissRight ? mViewWidth : -mViewWidth)
                            .Alpha (0)
                            .SetDuration (mAnimationTime)
                            .SetListener (new DownAnimatorListenerAdapter(this,downView,downPosition));
                    } 

                    else {
                        // cancel
                        mDownView.Animate()
                            .TranslationX(0)
                            .Alpha(1)
                            .SetDuration(mAnimationTime)
                            .SetListener(null);
                    }
                    mVelocityTracker.Recycle();
                    mVelocityTracker = null;
                    mDownX = 0;
                    mDownY = 0;
                    mDownView = null;
                    mDownPosition = ListView.InvalidPosition;
                    mSwiping = false;
                    break;
                }

            case MotionEventActions.Move: {
                    if (mVelocityTracker == null || mPaused) {
                        break;
                    }

                    mVelocityTracker.AddMovement(motionEvent);
                    float deltaX = motionEvent.RawX - mDownX;
                    float deltaY = motionEvent.RawY - mDownY;
                    if (Math.Abs(deltaX) > mSlop && Math.Abs(deltaY) < Math.Abs(deltaX) / 2)  {
                        mSwiping = true;
                        mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
                        mListView.RequestDisallowInterceptTouchEvent(true);

                        // Cancel ListView's touch (un-highlighting the item)
                        MotionEvent cancelEvent = MotionEvent.Obtain(motionEvent);
                        cancelEvent.Action= (Android.Views.MotionEventActions) ((int) MotionEventActions.Cancel |
                            ((int)motionEvent.ActionIndex <<(int) MotionEventActions.PointerIndexShift));
                        mListView.OnTouchEvent(cancelEvent);
                        cancelEvent.Recycle();
                    }

                    if (mSwiping) {
                        mDownView.TranslationX=deltaX - mSwipingSlop;
                        mDownView.Alpha=Math.Max(0f, Math.Min(1f,1f - 2f * Math.Abs(deltaX) / mViewWidth));
                        return true;
                    }
                    break;
                }
            }
            return false;
        }



        /**
     * Manually cause the item at the given position to be dismissed (trigger the dismiss
     * animation).
     */


        public void PerformDismiss(View dismissView, int dismissPosition) {
            // Animate the dismissed list item to zero-height and fire the dismiss callback when
            // all dismissed list item animations have completed. This triggers layout on each animation
            // frame; in the future we may want to do something smarter and more performant.
            ViewGroup.LayoutParams lp = dismissView.LayoutParameters;
            int originalHeight = dismissView.Height;
            ValueAnimator animator = ValueAnimator.OfInt (originalHeight, 1);
            animator.SetDuration(mAnimationTime);
            animator.AddListener (new DismissAnimatorListenerAdapter (this,originalHeight));

            animator.AddUpdateListener(new AnimatorUpdateListener(dismissView,lp));

            mPendingDismisses.Add(new PendingDismissData(dismissPosition, dismissView));
            animator.Start();
        }

        public void DismissAnimationEnded(int originalHeight)
        {
            --mDismissAnimationRefCount;
            if (mDismissAnimationRefCount == 0) {
                // No active animations, process all pending dismisses.
                // Sort by descending position
                mPendingDismisses.Sort ();
                //Collections.Sort(mPendingDismisses);

                int[] dismissPositions = new int[mPendingDismisses.Count];
                for (int i = mPendingDismisses.Count - 1; i >= 0; i--) {
                    dismissPositions[i] = mPendingDismisses[i].position;
                }
                mCallbacks.onDismiss(mListView, dismissPositions);

                // Reset mDownPosition to avoid MotionEvent.ACTION_UP trying to start a dismiss 
                // animation with a stale position
                mDownPosition = ListView.InvalidPosition;


                foreach (PendingDismissData pendingDismiss in mPendingDismisses) {
                    // Reset view presentation
                    pendingDismiss.view.Alpha=1f;
                    pendingDismiss.view.TranslationX=0;
                    var cc = pendingDismiss.view.LayoutParameters;
                    cc.Height = originalHeight;
                    pendingDismiss.view.LayoutParameters=cc;
                }

                // Send a cancel event
                long time = SystemClock.UptimeMillis();
                MotionEvent cancelEvent = MotionEvent.Obtain(time, time,MotionEventActions.Cancel, 0, 0, 0);
                mListView.DispatchTouchEvent(cancelEvent);

                mPendingDismisses.Clear();
            }
        }
    }

    class AnimatorUpdateListener : Java.Lang.Object, ValueAnimator.IAnimatorUpdateListener
    {
        View dismissView;
        ViewGroup.LayoutParams lp;
        public AnimatorUpdateListener (View dismissView, ViewGroup.LayoutParams lp)
        {
            this.dismissView = dismissView;
            this.lp = lp;
        }

        public void OnAnimationUpdate (ValueAnimator valueAnimator)
        {
            lp.Height = (int) valueAnimator.AnimatedValue;
            dismissView.LayoutParameters=lp;
        }
    }

    class DownAnimatorListenerAdapter:AnimatorListenerAdapter
    {
        View _downView; // mDownView gets null'd before animation ends
        int _downPosition ;
        SwipeDismissListViewTouchListener _parent;
        public DownAnimatorListenerAdapter(SwipeDismissListViewTouchListener parent,View downView,int downPosition)
        {
            _downView = downView;
            _parent = parent;
            _downPosition = downPosition;
        }

        public override void OnAnimationEnd (Animator animation)
        {
            base.OnAnimationEnd (animation);
            _parent.PerformDismiss (_downView, _downPosition);
        }
    }


    class DismissAnimatorListenerAdapter:AnimatorListenerAdapter
    {
        SwipeDismissListViewTouchListener _parent;
        int _originalHeight;
        public DismissAnimatorListenerAdapter(SwipeDismissListViewTouchListener parent,int originalHeight)
        {
            _parent = parent;
            _originalHeight = originalHeight;
        }

        public override void OnAnimationEnd (Animator animation)
        {
            base.OnAnimationEnd (animation);
            _parent.DismissAnimationEnded (_originalHeight);
        }
    }


    class PendingDismissData:IComparable<PendingDismissData> 
    {
        public int position;
        public View view;

        public PendingDismissData(int position, View view) {
            this.position = position;
            this.view = view;
        }

        public int CompareTo(PendingDismissData other) {
            // Sort by descending position
            return other.position - position;
        }
    }

}

UndoBarController.cs

using System;
using Android.Views;
using Android.Views;
using Android.OS;
using Android.Widget;
using Android.Text;
using Android.Runtime;
using Android.Animation;
using Java.Lang;
using Newtonsoft.Json;
using System.Collections.Generic;
using SwipeDismissListView;

namespace XpressCode
{
    public interface IUndoListener
    {
        void OnUndo (object obj);
    }

    public class UndoBarController
    {
        View mBarView;
        TextView mMessageView;
        ViewPropertyAnimator mBarAnimator;
        Handler mHideHandler = new Handler ();
        internal IUndoListener mUndoListener;

        // State objects
        internal object mUndoToken;
        string mUndoMessage;

        public UndoBarController (View undoBarView, IUndoListener undoListener)
        {
            mBarView = undoBarView;
            mBarAnimator = mBarView.Animate ();
            mUndoListener = undoListener;

            mMessageView = (TextView)mBarView.FindViewById (Resource.Id.undobar_message);
            mBarView.FindViewById<Button> (Resource.Id.undobar_button).Click += (object sender, EventArgs e) => {
                HideUndoBar (false);
                mUndoListener.OnUndo (mUndoToken);
            };
            HideUndoBar (true);
        }

        public void ShowUndoBar (bool immediate, string message, object undoToken)
        {
            mUndoToken = undoToken;
            mUndoMessage = message;
            mMessageView.Text = mUndoMessage;


            mBarView.Visibility = ViewStates.Visible;
            if (immediate) {
                mBarView.Alpha = 1;
            } else {
                mBarAnimator.Cancel ();
                mBarAnimator
                    .Alpha (1)
                    .SetDuration (mBarView.Resources.GetInteger (Android.Resource.Integer.ConfigShortAnimTime))
                    .SetListener (null);
            }

            t = new System.Timers.Timer (mBarView.Resources.GetInteger (Resource.Integer.undobar_hide_delay));
            t.Elapsed += HandleTimeoutElapsed;
            t.Start ();

        }


        void HandleTimeoutElapsed (object sender, System.Timers.ElapsedEventArgs e)
        {
            DisposeTimer ();
            HideUndoBar (false);
        }

        public void DisposeTimer()
        {
            lock(this)
            {
                if (t==null)
                    return;
                t.Stop ();
                t.Elapsed -= HandleTimeoutElapsed;
                t.Dispose ();
                t = null;
            }
        }

        System.Timers.Timer t;

        public void HideUndoBar (bool immediate)
        {
            DisposeTimer ();
            if (immediate) {
                mBarView.Visibility = ViewStates.Gone;
                mBarView.Alpha = 0;
                mUndoMessage = null;
                mUndoToken = null;

            } else {
                mBarAnimator.Cancel ();
                var animator = mBarAnimator
                    .Alpha (0)
                    .SetDuration (mBarView.Resources.GetInteger (Android.Resource.Integer.ConfigShortAnimTime))
                    .SetListener (new CloseAnimatorListenerAdapter (this));

            }
        }

        public void ClosePopup ()
        {
            mBarView.Visibility = ViewStates.Gone;
            mUndoMessage = null;
            mUndoToken = null;
        }

        public void onSaveInstanceState (Bundle outState)
        {
            outState.PutCharSequence ("undo_message", mUndoMessage);
            outState.PutString ("undo_token", JsonConvert.SerializeObject (mUndoToken));
        }

        public void onRestoreInstanceState (Bundle savedInstanceState)
        {
            if (savedInstanceState != null) {
                mUndoMessage = savedInstanceState.GetCharSequence ("undo_message");
                mUndoToken = JsonConvert.DeserializeObject<object> (savedInstanceState.GetString ("undo_token"));

                if (mUndoToken != null || !TextUtils.IsEmpty (mUndoMessage)) {
                    ShowUndoBar (true, mUndoMessage, mUndoToken);
                }
            }
        }
    }

    class CloseAnimatorListenerAdapter:AnimatorListenerAdapter
    {
        UndoBarController controller;

        public CloseAnimatorListenerAdapter (UndoBarController controller)
        {
            this.controller = controller;
        }

        public override void OnAnimationEnd (Animator animation)
        {
            controller.HideUndoBar (false);
        }
    }


}

Main.axml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            style="?android:listSeparatorTextViewStyle"
            android:text="@string/heading_dismissable_list_view" />
        <ListView
            android:id="@+id/lstItems"
            android:layout_weight="1"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/undobar"
        style="@style/UndoBar">
        <TextView
            android:id="@+id/undobar_message"
            style="@style/UndoBarMessage" />
        <Button
            android:id="@+id/undobar_button"
            style="@style/UndoBarButton" />
    </LinearLayout>
</FrameLayout>

Что я делаю неправильно? Спасибо за вашу помощь!

0 ответов

Другие вопросы по тегам