"main" prio=5 tid=1 MONITOR
| group="main" sCount=1 dsCount=0 obj=0x41920e88 self=0x4190f8d0
| sysTid=13407 nice=0 sched=0/0 cgrp=apps handle=1074618708
| state=S schedstat=( 50780242971 27570770290 130442 ) utm=4254 stm=824 core=0
at com.my.package.util.HandCards.onTouchEvent(SourceFile:~188)
- waiting to lock <0x45b91988> (a android.view.SurfaceView$4) held by tid=18 (Thread-14297)
at android.view.View.dispatchTouchEvent(View.java:7837)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1917)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2075)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1522)
at android.app.Activity.dispatchTouchEvent(Activity.java:2458)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2023)
at android.view.View.dispatchPointerEvent(View.java:8017)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:3966)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3845)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3405)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3455)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3424)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3531)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3432)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3588)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3405)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3455)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3424)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3432)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3405)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5554)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5534)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5505)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5634)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:138)
at android.os.Looper.loop(Looper.java:196)
at android.app.ActivityThread.main(ActivityThread.java:5135)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:878)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
at dalvik.system.NativeStart.main(Native Method)
...
"Thread-14297" prio=5 tid=18 SUSPENDED
| group="main" sCount=1 dsCount=0 obj=0x45ba6358 self=0x76036b38
| sysTid=21120 nice=0 sched=0/0 cgrp=apps handle=1979936656
| state=S schedstat=( 48296386737 3088012659 22649 ) utm=4691 stm=138 core=0
#00 pc 00021adc /system/lib/libc.so (__futex_syscall3+8)
#01 pc 0000f074 /system/lib/libc.so (__pthread_cond_timedwait_relative+48)
#02 pc 0000f0d4 /system/lib/libc.so (__pthread_cond_timedwait+64)
#03 pc 0005655f /system/lib/libdvm.so
#04 pc 00056b21 /system/lib/libdvm.so (dvmChangeStatus(Thread*, ThreadStatus)+34)
#05 pc 00050fd7 /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*)+406)
#06 pc 00000214 /dev/ashmem/dalvik-jit-code-cache (deleted)
at android.graphics.Canvas.native_drawBitmap(Native Method)
at android.graphics.Canvas.drawBitmap(Canvas.java:1202)
at com.my.package.util.HandCards.a(SourceFile:178)
at com.my.package.util.HandCards.onDraw(SourceFile:136)
at com.my.package.util.d.run(SourceFile:36)
synchronized (mRenderThread.getSurfaceHolder()) {
canvas.drawBitmap(drawCardBitmap, null, mDrawingRect, mGraphicsPaint);
SurfaceView
子类的完整代码是:
public class HandCards extends SurfaceView implements SurfaceHolder.Callback {
/** Opacity of the shadow layer that hides other cards when one card is highlighted and covers all cards when it's another player's turn (where 0 is transparent and 255 is opaque) */
private static final int SHADOW_ALPHA = 150;
private static SparseArray<Bitmap> mCardCache = new SparseArray<Bitmap>(); // cache array for own card bitmaps
private HandThread mRenderThread;
private volatile List<Card> mCards;
private volatile int mCardCount;
private volatile int mScreenWidth;
private volatile int mScreenHeight;
private volatile int mCardWidth;
private volatile int mCardHeight;
private volatile int mHighlightedCard = -1;
private CardClickCallback mCardClickCallback;
private volatile int mBlattID = 1;
private volatile int mCurrentCardSpacing;
private final Paint mGraphicsPaint;
private final Paint mShadowPaint;
private final Rect mDrawingRect;
private volatile int mTouchEventAction;
private volatile int mTouchEventCard;
private Bitmap drawCardBitmap;
private volatile int mOnDrawX1;
private final BitmapFactory.Options mBitmapOptions;
private volatile boolean mIsActive = true;
private final int[] mCardSelection = new int[GameState.MAX_SWAP_CARDS];
/** Indicates that the card view is currently used for choosing some cards to create a selection */
private volatile boolean mIsChooseMode;
/** Holds the index of the selected card that will be replaced next if all selection slots are full */
private volatile int mNextReplacePosition;
/** Used only locally in drawCard() but is declared here to save repeated allocations */
private volatile int mCardOffsetY;
private volatile int mRequiredSelectionCount;
public HandCards(Context activityContext, AttributeSet attributeSet) {
super(activityContext, attributeSet);
getHolder().addCallback(this);
setFocusable(true); // touch events should be processed by this class
mCards = new ArrayList<Card>();
mGraphicsPaint = new Paint();
mGraphicsPaint.setAntiAlias(true);
mGraphicsPaint.setFilterBitmap(true);
mShadowPaint = new Paint();
mShadowPaint.setARGB(SHADOW_ALPHA, 20, 20, 20);
mShadowPaint.setAntiAlias(true);
mBitmapOptions = new BitmapFactory.Options();
mBitmapOptions.inInputShareable = true;
mBitmapOptions.inPurgeable = true;
mDrawingRect = new Rect();
}
public Card getCard(int location) throws Exception {
if (mCards != null) {
synchronized (mCards) {
return mCards.get(location); // card may not be found (throw exception then)
}
}
return null;
}
public static Bitmap cardCacheGet(int key) {
synchronized (mCardCache) {
return mCardCache.get(key);
}
}
public static void cardCachePut(int key, Bitmap object) {
synchronized (mCardCache) {
mCardCache.put(key, object);
}
}
public int[] getSelectedCards() {
return mCardSelection;
}
public void setActive(boolean active) {
if (mCardSelection != null) {
for (int i = 0; i < GameState.MAX_SWAP_CARDS; i++) { // loop through all slots for selected cards
mCardSelection[i] = -1; // unset the slot so that it is empty by default
}
}
mIsActive = active;
}
public boolean isActive() {
return mIsActive;
}
public void setChooseMode(boolean active, int swapCardCount) {
mNextReplacePosition = 0;
mIsChooseMode = active;
mRequiredSelectionCount = swapCardCount;
}
public boolean isChooseMode() {
return mIsChooseMode;
}
public void stopThread() {
if (mRenderThread != null) {
mRenderThread.setRunning(false);
}
}
@Override
public void onDraw(Canvas canvas) {
if (canvas != null) {
synchronized (mCards) {
mCardCount = mCards.size();
canvas.drawColor(Color.BLACK);
if (mCardCount > 0) {
mCurrentCardSpacing = Math.min(mScreenWidth/mCardCount, mCardWidth);
for (int c = 0; c < mCardCount; c++) {
if (c != mHighlightedCard || !isActive()) {
try {
drawCard(canvas, mCards.get(c).getDrawableID(mBlattID), false, c*mCurrentCardSpacing, c*mCurrentCardSpacing+mCardWidth, c);
}
catch (Exception e) { }
}
}
if (mHighlightedCard > -1 && isActive()) {
mOnDrawX1 = Math.min(mHighlightedCard*mCurrentCardSpacing, mScreenWidth-mCardWidth);
try {
drawCard(canvas, mCards.get(mHighlightedCard).getDrawableID(mBlattID), true, mOnDrawX1, mOnDrawX1+mCardWidth, mHighlightedCard);
}
catch (Exception e) { }
}
else if (!isActive()) {
drawCard(canvas, 0, true, 0, mScreenWidth, 0);
}
}
}
}
}
private void drawCard(Canvas canvas, int resourceID, boolean highlighted, int xLeft, int xRight, int cardPosition) {
if (canvas != null) {
try {
if (highlighted) {
canvas.drawRect(0, 0, mScreenWidth, mScreenHeight, mShadowPaint);
}
if (resourceID != 0) {
drawCardBitmap = cardCacheGet(resourceID);
if (drawCardBitmap == null) {
drawCardBitmap = BitmapFactory.decodeResource(getResources(), resourceID, mBitmapOptions);
cardCachePut(resourceID, drawCardBitmap);
}
mCardOffsetY = 0; // by default draw all cards right at the bottom (without highlighting by position)
if (mCardSelection != null) {
for (int i = 0; i < GameState.MAX_SWAP_CARDS; i++) { // loop through all slots for selected cards
if (mCardSelection[i] == cardPosition) { // if current card has been selected (in that slot)
mCardOffsetY = mScreenHeight*1/4; // lift the card by one quarter to highlight it
break; // card has already been detected to be selected so stop here
}
}
}
mDrawingRect.set(xLeft, mCardOffsetY, xRight, mCardHeight+mCardOffsetY);
canvas.drawBitmap(drawCardBitmap, null, mDrawingRect, mGraphicsPaint);
}
}
catch (Exception e) { }
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mRenderThread == null) { return false; }
synchronized (mRenderThread.getSurfaceHolder()) { // synchronized so that there are no concurrent accesses
mTouchEventAction = event.getAction();
if (isActive()) {
if (mTouchEventAction == MotionEvent.ACTION_DOWN || mTouchEventAction == MotionEvent.ACTION_MOVE) {
if (event.getY() >= 0 && event.getY() < mScreenHeight) {
mTouchEventCard = (int) event.getX()/mCurrentCardSpacing;
if (mTouchEventCard > -1 && mTouchEventCard < mCardCount) {
mHighlightedCard = mTouchEventCard;
}
else {
mHighlightedCard = -1;
}
}
else {
mHighlightedCard = -1;
}
}
else if (mTouchEventAction == MotionEvent.ACTION_UP) {
if (mCardClickCallback != null && mHighlightedCard > -1 && mHighlightedCard < mCardCount) {
if (isChooseMode()) { // card has been chosen as a swap card
int freeSelectionIndex = -1; // remember the index of a free selection slot (default = none available)
for (int i = 0; i < mRequiredSelectionCount; i++) { // loop through all allowed slots for selected cards
if (mCardSelection[i] == mHighlightedCard) { // if this card has already been selected
mCardSelection[i] = -1; // unselect the card
freeSelectionIndex = -2; // mark that there is no need to select a new card
break; // slot of current card has already been found so stop here
}
else if (mCardSelection[i] == -1 && freeSelectionIndex == -1) { // if slot is still available and no free slot has been found yet
freeSelectionIndex = i; // remember the index of this free slot
}
}
if (freeSelectionIndex > -2) { // if a new card is to be placed in the selection array
if (freeSelectionIndex >= 0) { // if a free slot was available
mCardSelection[freeSelectionIndex] = mHighlightedCard; // just place the card there
}
else { // if no free slot was available anymore
mCardSelection[mNextReplacePosition] = mHighlightedCard; // replace another card in one of the slots
mNextReplacePosition = (mNextReplacePosition+1) % mRequiredSelectionCount; // advance the cursor that points to the slot which will be replaced next
}
}
}
else { // card has been selected to be played on the table
try {
mCardClickCallback.chooseCard(mCards.get(mHighlightedCard));
}
catch (Exception e) {
// index was out of mCards' bounds (just ignore this, user may tap on card again)
}
}
}
mHighlightedCard = -1;
}
}
else {
try {
mCardClickCallback.resyncManually();
}
catch (Exception e) { }
}
}
return true;
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { }
public void setCards(List<Card> currentCards) {
synchronized (mCards) {
mCards.clear();
mCards.addAll(currentCards);
}
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
mScreenWidth = getWidth();
mScreenHeight = getHeight();
mCardHeight = mScreenHeight;
mCardWidth = mCardHeight*99/150;
mCurrentCardSpacing = mCardWidth;
mRenderThread = new HandThread(getHolder(), this);
mRenderThread.setRunning(true);
mRenderThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
mRenderThread.setRunning(false); // stop thread
while (retry) { // wait for thread to close
try {
mRenderThread.join();
retry = false;
}
catch (InterruptedException e) { }
}
}
public synchronized void setCardClickCallback(CardClickCallback callback) {
mCardClickCallback = callback;
}
public void setBlattID(int blattID) {
mBlattID = blattID;
}
}
然后,还有呈现线程:
public class HandThread extends Thread {
private final SurfaceHolder mSurfaceHolder;
private final HandCards mSurface;
private volatile boolean mRunning = false;
public HandThread(SurfaceHolder surfaceHolder, HandCards surface) {
mSurfaceHolder = surfaceHolder;
mSurface = surface;
}
public SurfaceHolder getSurfaceHolder() {
return mSurfaceHolder;
}
public void setRunning(boolean run) {
mRunning = run;
}
@Override
public void run() {
Canvas c;
while (mRunning) {
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if (c != null) {
mSurface.onDraw(c);
}
}
}
finally { // when exception is thrown above we may not leave the surface in an inconsistent state
if (c != null) {
try {
mSurfaceHolder.unlockCanvasAndPost(c);
}
catch (Exception e) { }
}
}
}
}
}
发生ANR是因为您的OnTouchEvent()
方法正在TID=18持有的锁上同步,该锁是一个未命名的线程,仅知为Thread-14297。
许多人遵循这样的模式,在锁定SurfaceView画布的同时,他们也锁定了SurfaceHolder对象。在具有公共可见性的对象上进行同步是一个坏主意,在与GUI框架共享的对象上进行同步更是一个更糟糕的主意,因此这种模式持续存在是令人遗憾的。(但我离题了。)
您正在一个重写的ondraw()
方法中进行绘制,如果从呈现器线程进行绘制,这就没有意义了--视图层次结构使用了ondraw()
方法,并将从UI线程调用,但这里显然是从其他地方调用的。您应该将其称为其他名称,也许只是mydraw()
。(但我离题了。)
嗨,我是 Android 编程的新手,我一直在看一个关于 Android 游戏编程的 youtube 教程,问题是这个特定的课程被留在了期中,所以游戏还没有完成,所以我尝试自己添加一些东西。在我的新旅程中,我一直在解决一个自己无法解决的问题,所以如果有人可以帮助我,我会在这里尝试。 问题是游戏的视觉部分,我有一个surfaceView和一个TouchEvent事件。当我暂停游戏(方法暂停活动)时出
问题内容: 我正在尝试将一些 非常简单的 图形绘制成a,但是我无法使其正常工作。没有例外,但我也看不到任何结果。 更确切地说,我正在尝试创建一个并用单一颜色填充它。 这是我的代码: 不出所料,我得到了 Drawing … 输出两次。 有人可以建议为什么我可能无法提取吗? 问题答案: 我删除了代理类,并将其全部移至我的活动中。现在正在工作。固定代码如下:
本文向大家介绍Android Surfaceview的绘制与应用,包括了Android Surfaceview的绘制与应用的使用技巧和注意事项,需要的朋友参考一下 Android Surfaceview的绘制与应用 一.surfaceview与view的区别 Android 提供了view进行视图的绘制,可以满足大部分的会图需求,但在有些时候却是心有余而力不足。我们知道,view通过刷新来绘
本文向大家介绍解决Android SurfaceView绘制触摸轨迹闪烁问题的方法,包括了解决Android SurfaceView绘制触摸轨迹闪烁问题的方法的使用技巧和注意事项,需要的朋友参考一下 本文分享了解决SurfaceView触摸轨迹闪烁问题的方法,供大家参考,具体内容如下 第一种解决SurfaceView触摸轨迹闪烁问题的方法: 由于SurfaceView使用双缓存机制,两张画布轮流显
本文向大家介绍Android截屏SurfaceView黑屏问题的解决办法,包括了Android截屏SurfaceView黑屏问题的解决办法的使用技巧和注意事项,需要的朋友参考一下 最近项目中有截屏的需求,普通的view截屏方法网上一搜一大把,但是SurfaceView截屏黑屏问题很多文章说的并不清楚,自己参考了一些别的博客,再加上自己的思考,算是找到了一种解决方案。 1、首先看我们一般是怎么用Su
本文向大家介绍Android中SurfaceView和view画出触摸轨迹,包括了Android中SurfaceView和view画出触摸轨迹的使用技巧和注意事项,需要的朋友参考一下 一、引言 想实现一个空白的画板,上面可以画出手滑动的轨迹,就这么一个小需求。一般就来讲就两种实现方式,view或者surfaceview。下面看看两种是如何实现的。 二、实现原理