我正在为我的纸牌游戏创建移动动画,为此,我创建了一个自定义表面视图,同时在我的表面视图中调用invalidate方法时,我正在遵循异常
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;
}
}
Stacktrace:
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)
答案 0 :(得分:-1)
只有创建视图层次结构的原始线程可以触摸其视图。
每当您更新视图时,都需要在线程代码中使用RunOnUiThread
:
RunOnUiThread (() => {
someView.SomeProperty = "SO";
});
re:https://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)