在自定义表面视图中调用无效函数时获取 ViewRootImpl$CalledFromWrongThreadException [英] Getting ViewRootImpl$CalledFromWrongThreadException when calling invalidate function in Custom Surface View

查看:21
本文介绍了在自定义表面视图中调用无效函数时获取 ViewRootImpl$CalledFromWrongThreadException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的纸牌游戏创建一个移动动画,为此我创建了一个自定义表面视图,而在我的表面视图中调用 invalidate 方法时,我遇到了以下异常

I am creating a moving animation for my Card Game, for this i have created a custom surface view, while calling invalidate method inside my Surface View i am getting following exception

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

我的代码:线程类

 class MySurfaceViewThread:BaseThread
{
    private MySurfaceView mysurfaceview;
    private ISurfaceHolder myThreadSurfaceHolder;
    bool running;

    public MySurfaceViewThread(ISurfaceHolder paramSurfaceHolder, MySurfaceView paramSurfaceView)
    {
        mysurfaceview = paramSurfaceView;
        myThreadSurfaceHolder = paramSurfaceHolder;

    }
    public override void RunThread()
    {
        Canvas c;
        while (running)
        {
            c = null;
            try
            {
                c = myThreadSurfaceHolder.LockCanvas(null);
                 mysurfaceview.Render(c);
             mysurfaceview.PostInvalidate();


            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
            }
            finally
            {
                if (c != null)
                {
                    myThreadSurfaceHolder.UnlockCanvasAndPost(c);   
                }
             //   running = false;
            }

        }

    }
    public override void SetRunning(bool paramBoolean)
    {
        running = paramBoolean;
    }
}

表面视图类

  class MySurfaceView : SurfaceView, ISurfaceHolderCallback
{
    ISurfaceHolder holder;
    MySurfaceViewThread thread;
    Context context;
    Deck DealtDeck;

    DisplayMetrics metrics;
    int Screen_Center_X;
    int Screen_Center_Y;
    int Screen_Width;
    int Screen_Height;
    int Screen_Top_Middle_X;
    int Screen_Top_Middle_Y;
    int Screen_Bottom_Middle_X;
    int Screen_Bottom_Middle_Y;
    float density;
    int Card_Width;
    int Card_Height;
    int Down_Card_Gap;
    Deck DiscardedDeck;
    Deck MainPlayer;
    int localdownanimationvalue=0;
    Bitmap localimage;
    Bitmap rotatedimage;
    Cards localcard;
    public MySurfaceView(Context context):base(context)
    {
        this.context = context;
        metrics = Resources.DisplayMetrics;
        SetWillNotDraw(false);
        Init();

    }

    public MySurfaceView(Context context, IAttributeSet attrs):base(context, attrs)
    {
     this.context=context;
        metrics = Resources.DisplayMetrics;
        SetWillNotDraw(false);
        Init();
    }
    private void Init()
    {
        Console.WriteLine("Init method start");
       // SurfaceView surfaceview = this;
        holder = Holder;
        holder.AddCallback(this);
        this.thread = new MySurfaceViewThread(holder,this);
        Focusable=true;


    }

    public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height)
    {
        //throw new NotImplementedException();
    }

    public void SurfaceCreated(ISurfaceHolder holder)
    {
        this.thread.SetRunning(true);
        this.thread.Start();
        Initializevariable();
        AllocatedCardList();

        SetWillNotDraw(false);

    }

    private void Initializevariable()
    {
        Screen_Width = metrics.WidthPixels;
        Screen_Height = metrics.HeightPixels;
        density = metrics.Density;
        Card_Width = (int)(125.0F * density);
        Card_Height = (int)(93.0F * density);
        Screen_Center_X = Screen_Width / 2;
        Screen_Center_Y = Screen_Height / 2;
        Screen_Top_Middle_X = Screen_Center_X - Card_Width;
        Screen_Top_Middle_Y = Screen_Center_Y - Card_Height;
        Screen_Bottom_Middle_X = Screen_Center_X - Card_Width/2;
        Screen_Bottom_Middle_Y = Screen_Height - Card_Height;
        DealtDeck = new Deck();
        MainPlayer = new Deck();
     //   FaceDownDeck = new Deck(Screen_Center_X - Card_Width/2, Screen_Center_Y- Card_Height/2);
    }


    public void SurfaceDestroyed(ISurfaceHolder holder)
    {
        bool retry = true;
        this.thread.SetRunning(false);
       while(retry)
        {
            thread.Join();
            retry = false;
        }
    }

    void AllocatedCardList()
    {
        Cards localcard;

        //Allocate all cards to dealtdeck first
        for (int i = 1; i <= 13; i++)
        {
            for (int j = 1; j <= 4; j++)
            {
                DealtDeck.Add(new Cards((Cards.Rank)i, (Cards.SuitType)j, true, (Screen_Center_X - Card_Width / 2), (Screen_Center_Y - Card_Height / 2)));
            }
        }
        //Allocate to bottom player starting card should be bottom-center
        localcard = DealtDeck.RemoveCard();
        localcard.current_X = Screen_Bottom_Middle_X;
        localcard.current_Y = Screen_Bottom_Middle_Y;
        MainPlayer.Add(localcard);



    }
    public  void Render(Canvas paramCanvas)
    {

        try
        {
          //  
            localcard = DealtDeck.getCard();
         if (localdownanimationvalue <= Screen_Height)
            {
                paramCanvas.DrawColor(Android.Graphics.Color.Transparent, PorterDuff.Mode.Clear);
                localimage = DecodeSampledBitmapFromResource(Resources, localcard.GetImageId(context), Card_Width, Card_Height);
                rotatedimage = RotateBitmap(localimage, 180);
                paramCanvas.DrawBitmap(rotatedimage, Screen_Center_X, localdownanimationvalue, null);
                Updatedowncardvalue();
            }



        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.ToString());
        }

    }

    protected override void OnDraw(Canvas paramCanvas)
    {



    }

    private void Updatedowncardvalue()
    {
        const int updatevalue = 50;
        if (localdownanimationvalue + updatevalue > Screen_Height)
            localdownanimationvalue = Screen_Height;
        else
            localdownanimationvalue = localdownanimationvalue + updatevalue;

        Invalidate();
    }

    private Bitmap DecodeSampledBitmapFromResource(Resources resources, int cardid, int card_Width, int card_Height)
    {
        BitmapFactory.Options options = new BitmapFactory.Options
        {
            InJustDecodeBounds = true
        };
        Bitmap image = BitmapFactory.DecodeResource(resources, cardid,options);
        options.InSampleSize = CalculateInSampleSize(options, card_Width, card_Height);
        options.InJustDecodeBounds = false;
        return BitmapFactory.DecodeResource(resources, cardid, options);
    }

    private int CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
    {

        // Raw height and width of image
       int width = options.OutWidth;
        int height = options.OutHeight;
        int samplesize = 1;
        if(height > reqHeight || width > reqWidth)
        {
            // Calculate ratios of height and width to requested height and width
            int heightratio = (int)Math.Round((double)height / reqHeight);
            int widthratio = (int)Math.Round((double)width / reqWidth);
            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            samplesize = heightratio < widthratio ? widthratio : heightratio;

        }
        return samplesize;
    }

    private Bitmap RotateBitmap(Bitmap localimage, float angle)
    {

        Matrix matrix = new Matrix();
        matrix.PostRotate(angle);

        Bitmap resized= Bitmap.CreateBitmap(localimage, 0, 0, localimage.Width, localimage.Height, matrix, true);
        localimage.Recycle();
        return resized;

    }
}

堆栈跟踪:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6462)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:932)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:4692)
at android.view.View.invalidateInternal(View.java:11806)
at android.view.View.invalidate(View.java:11770)
at android.view.View.invalidate(View.java:11754)

推荐答案

只有创建视图层次结构的原始线程才能触及其视图.

Only the original thread that created a view hierarchy can touch its views.

每当您更新视图时,您都需要在线程代码中使用 RunOnUiThread:

You need to use RunOnUiThread from within your thread code whenever you update your views:

RunOnUiThread (() => {
    someView.SomeProperty = "SO";
});

re: https://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)

这篇关于在自定义表面视图中调用无效函数时获取 ViewRootImpl$CalledFromWrongThreadException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆